SiTE - Simple Template Engine
Author: Peter Conrad, conrad@tivano.de, Frederik Dahlke, dahlke@tivano.de
$Id: documentation.html,v 1.11 2002/05/03 08:47:35 dahlke Exp $
1. Building SiTE
If you're using the binary distribution you don't have to build anything.
Simply include the "site.jar" in your classpath.
The source distribution contains several directories, some of which have
build.xml scripts that can be used with
ANT. Just run
ant compile
in the top-level directory of the distribution to compile the API classes and
create the "site.jar". JDK 1.2 or higher is required for SiTE.
To run the unit tests in the "test" subdirectory you'll need the JUnit module
for NetBeans.
For the "ULoseXMLC" demo class (which we used to
compare SiTE to XMLC) you need the
Enhydra Multiserver.
Please note that you don't need either JUnit or Enhydra to use SiTE - these
were only used to run some tests. SiTE itself was designed to run without any
additional libraries!
2. Using SiTE
2.1 How does SiTE work?
SiTE parses a template and generates a document representation. The
document is represented as a tree structure. The application then modifies
the document tree. Finally, the document tree is converted back into text.
SiTE generates a SiTE node in the document tree for every area which has
to be manipulated. Everything else is stored in BLOB nodes.
In other words, BLOB nodes are containers for any kind of data
between SiTE nodes. BLOB nodes are always leaf nodes.
Conceptionally, what you do with SiTE does not differ much from what
you can do with a text editor. You mark an area in your template.
You are then able to cut (i. e. remove) the area, you can copy it or replace
it with anything you like.
In SiTE, the marked area is represented as a node.
2.2 Standard template syntax
The SiTE template syntax is configurable. So if you do not
like the standard syntax you are free to define another template syntax.
You can do so by using and configuring the ConfigurableParser or
ConfigurableHtmlParser class.
There are four parser classes contained in the SiTE distribution. The
SimpleTextParser is the fastest parser and provides no convenience
template syntax for HTML. The ConfigurableHtmlParser is a parser with
a configurable syntax and convenience support for HTML templates but is
not as fast as the SimpleTextParser. (All parsers are a lot faster than
JSP recompilation.)
2.2.1 SimpleTextParser and ConfigurableTextParser
Each area which has to be manipulated is marked with a
<SITE SNAME="manipulateme"> tag at the beginning and a
</SITE> at the end.
For instance an email template may look like this:
Dear <SITE SNAME="customerName">example customer</SITE>,
have you seen our new template engine? .......
SiTE then builds a document tree from the template. Take a look at the
document tree for this template. Now, what
would you do to replace the customer name? You will need to replace the
customer name area which is represented as a node
that contains "example customer" with a BLOB node which contains the
current customer's name:
// access the node named "customerName"
customerNameNode = myMailTemplate.getNode("customerName");
// replace all of its child nodes with a new blob node containing
// "Jim Solaris"
customerNameNode.replaceAll("Jim Solaris");
If you convert myMailTemplate to a string, the result
will be:
Dear Jim Solaris,
have you seen our new template engine? .......
As you can see the SiTE template contains absolutely no application code. This
is good because you can change your template in any way you can think of.
As long as the template still contains the SiTE tags they will be replaced
by the customer's name, no matter if your template is in HTML, XML,
WML, LaTeX, plaintext or any other format. Plus your template designers don't
have to care about any Java-code or custom scripting language code which
messes up their templates and makes maintenance expensive.
With the ConfigurableTextParser it is possible to define your own
template syntax. If you do not like the idea of including <SITE>-tags you
can change the template syntax:
Dear <!--SITE MYATTRIBUTE="customerName"-->example customer<!--/SITE-->,
have you seen our new template engine? .......
2.2.2 SimpleHtmlParser and ConfigurableHtmlParser
These parsers support an extended
syntax in order to make it more convenient to work with HTML.
Let's say you have an anchor element in an HTML-template and want
to modify the HREF attribute and the link's text. In this case you can use
the SNAME-attribute:
<a href="mylink.html" SNAME="link">my link text</a>
(Note that the attribute values must be enclosed in double quotes!)
Take a look at the document tree.
You can then modify the HREF and any other attribute of the anchor tag and
the link text via the SiTE-API.
Note: If you mark a *ML tag using the SNAME-attribute which
contains other *ML tags, you can only access the attributes of the marked *ML tag,
not its content. In the following example you will only be able to modify
the anchor tag's attributes:
<a href="mylink.html" SNAME="link"><b>my link text</b></a>
Take a look at the document tree.
A solution for this is to provide additonal SITE tags
<a href="mylink.html" SNAME="link"><b><SITE SNAME="text"
>my link text</SITE></b></a>
another solution is to rearrange your HTML
<b><a href="mylink.html" SNAME="link">my link text</a></b>
Take a look at the document tree.
2.3 An example
We have a HTML-page containing a list template. The list consists of
two entry types. One for external links colored red and one for internal
links colored green. The application's task is to fill the list with data provided
from some data source.
2.3.1 Template
The HTML code:
<table border=2>
<tr>
<td bgcolor=#00ff00>entry 1</td>
<td bgcolor=#00ff00><a
href=locallink.html>link</a
></td>
</tr>
<tr>
<td bgcolor=#ff0000>entry 2</td>
<td bgcolor=#ff0000><a
href=http://www.tivano.de/>external link</a
></td>
</tr>
</table>
Now let's get it working together with SiTE. We need to mark the
table rows (we want to copy them for each entry) and the HTML-tags
which need to be filled with data:
<table border=2>
<SITE SNAME="localRow">
<tr>
<td bgcolor="#00ff00" SNAME="rowEntry">entry 1</td>
<td bgcolor=#00ff00><a
href="locallink.html" SNAME="link">link</a
></td>
</tr>
</SITE>
<SITE SNAME="extRow">
<tr>
<td bgcolor="#ff0000" SNAME="rowEntry">entry 2</td>
<td bgcolor=#ff0000><a href="http://www.tivano.de/"
SNAME="link">external link</a
></td>
</tr>
</SITE>
</table>
The SITE template nested in this document:
Take a look at the document tree.
2.3.2 Application
The processing of a template takes four steps:
- initialize the TemplateFactory
- fetch the template
- manipulate the template
- output the template
2.3.2.1 Initialize the TemplateFactory
The TemplateFactory manages all templates. Getting templates
via a TemplateFactory is the preferred method, because the factory automatically
handles the underlying storage system, parsing and caching. In most cases the
TemplateFactory will
be initialized with a properties object containing the template
directory configuration:
TemplateFactory gtf =
TemplateFactory.getTemplateFactory(
"/my/properties/file.properties");
The TemplateFactory can be configured in many ways. For now just remember it needs
to be initialized. The configuration will be explained in detail in the architecture
and configuration section.
2.3.2.2 Fetch a template
The TemplateFactory returns a template-tree. You can access the template
tree via its root node. The flowchart for getTemplate is
here.
Node rootNode = (Node) gtf.getTemplate("mytemplate.html");
2.3.2.3 Manipulate the template
Template manipulation is nothing but tree manipulation. SiTE builds a tree
structure for every template.
The TemplateFactory returns the root node for a template.
You can access any child node with a name (any node with SNAME="nodename") via
the node.getNode("nodename") function. So you can access any named node
of the template via the rootNode.
The code for
filling the template from the example above looks like this: (The complete
servlet source code is available in the distribution in the examples
directory.)
try {
// get the template from the GenericTemplateFactory
Node rootNode = (Node) gtf.getTemplate(this.template);
// you would normally call a business function here
MyLink[] myData = this.getData();
Node localLinkRow = rootNode.getNode("localRow");
Node extLinkRow = rootNode.getNode("extRow");
Node newNode;
for (int i = 0; i < myData.length; i++) {
// choose the node to copy, depending on the link type
if (myData[i].isLocalLink()) {
newNode = localLinkRow.copy();
} else {
newNode = extLinkRow.copy();
}
// set the text for first column
Node rowEntry = newNode.getChild("rowEntry");
rowEntry.replaceAll(myData[i].getText());
// set the link's href
Node link = newNode.getChild("link");
link.setAttribute("href", myData[i].getHref());
// set link text
link.replaceAll(myData[i].getLinkText());
// insert new node in template
rootNode.insertBefore(localLinkRow, newNode);
}
// remove unneeded nodes
rootNode.removeNode(localLinkRow);
rootNode.removeNode(extLinkRow);
response.setContentType(contentType);
outputTemplate(request, response, rootNode);
} catch (RecompilationException e) {
throw new ServletException(e);
}
2.3.2.4 output the template
At the end you just need to convert the template to string
and write it to your output stream:
myStream.print(rootNode.toString());
2.3.2.5 code reuse
You can reuse the Java code above for any template matching the structure
of the original template. Excellent examples for this are the url rewriting
and the automatic form filling classes. The de.tivano.site.util.FormFiller
class for example recognizes different form input elements and automatically
fills in values provided by the HttpServletRequest or a Hashtable. This
becomes a very powerful framework when used together with
Facade, Tivano's
web application framework. Using Facade you can associate handler classes
with SiTE nodes.
2.4 Meta information
After parsing a template, the TemplateFactory can check if it fulfills
certain constraints. The constraints could for instance require that a template
contains a SITE tag with SNAME="customerName".
Meta information is useful if you want to check if your templates
match a structure your application expects it to have (if it doesn't, the
result of the application processing the template may be broken).
This provides a simple form of automated quality assurance for your templates.
2.4.1 TAGSPEC and ATTRSPEC
ATTRSPECs define a set of allowed values for an attribute. It is
possible to define global ATTRSPECs and ATTRSPECs that apply within certain tags
only.
A TAGSPEC defines several things:
- The template must contain a tag matching it.
- Any tag matching it must contain other tags which match the
TAGSPECs enclosed in it.
- Any tag matching it must contain additional attributes as defined in the
NAMELIST part.
- Attributes of tags enclosed in a matching tag may only contain the values
defined in the ATTRSPEC part.
2.4.2 Metainfo grammar
ALPHA := 'A' ... 'Z' 'a' ... 'z' '_'
DIGIT := '0' ... '9'
ALPHANUM := ALPHA | DIGIT | '-'
SPECIAL := ' ' | ':' | '.' | ',' | ';'
WS := ' ' | <tab> | <newline>
OPTWS := WS*
SOMEWS := WS OPTWS
NAME := ALPHA ALPHANUM*
NAMELIST := '(' OPTWS NAME (SOMEWS NAME)* OPTWS ')'
VALUE := '"' (ALPHANUM | SPECIAL)* '"'
VALUELIST := '(' OPTWS VALUE (SOMEWS VALUE)* OPTWS ')'
ATTRSPEC := NAME OPTWS ':' OPTWS VALUELIST
ATTRVALUE := NAME '=' VALUE
TAGSELECT := '(' ATTRVALUE (SOMEWS ATTRVALUE)* ')'
TAGSPEC := TAGSELECT OPTWS ':' OPTWS NAMELIST OPTWS '(' METAINFO ')'
METAINFO := (ATTRSPEC | OPTWS) (SOMEWS ATTRSPEC)* (SOMEWS TAGSPEC)* OPTWS
2.4.3 Example
Consider the following meta information:
ART: ("title" "something")
(NAME="row") : () (
(NAME="col1") : () ()
(NAME="col2") : () ()
)
The first row is an ATTRSPEC. All attributes ART must contain either the value
"title" or "something".
The other rows form a TAGSPEC. They require a tag with NAME=row to be present
in the template. The row tag must contain two tags, one named "col1" and another
named "col2".
3. Installation and configuration
3.1 Add SiTE to your CLASSPATH
In order to use SiTE in your application, you will have to add the site.jar
to your application CLASSPATH.
3.2 Configuration
SiTE is configured by Java properties. In order to understand the
configuration, you will need to understand the basic architecture of SiTE.
3.2.1 SiTE componentes
The central object in SiTE is the TemplateFactory. The TemplateFactory
uses four interfaces:
- SourceRepository
A SourceRepository fetches template files from a
data source (like the filesystem or a database) and delivers
them to the TemplateFactory which then calls the TemplateParser.
The default SourceRepository is
the FilesystemSourceRepository which fetches template files from
the file system.
- MetaRepository (optional)
A MetaRepository stores constraints in the form of MetaInfo
objects. See section 2.4 for a description of meta information.
- TemplateParser
A TemplateParser parses template source code (as delivered by
a SourceRepository) and builds a document
tree which represents the template.
SiTE comes with two parsers, a SimpleTextParser and a SimpleHtmlParser
(see section 2.2).
- TemplateCache (optional)
A TemplateCache stores document representations (as returned
by the TemplateParser). The
TemplateFactory tries to retreive a template from the TemplateCache
before it calls the TemplateParser.
You can provide your own implementations of these interfaces, or simply use
some of the default implementations (which may not always suit your needs).
See the following section on how to integrate your own implementations with
the TemplateFactory.
3.2.2 Basic configuration properties
The TemplateFactory.getTemplateFactory() method by default
creates a de.tivano.site.generic.GenericTemplateFactory and
either takes a properties object or the filename of a properties file as an
argument. The properties define
which classes the TemplateFactory should use as its TemplateParser,
SourceRepository, MetaRepository and TemplateCache:
//linebreaks for better legibility
de.tivano.site.generic.genericTemplateFactory.parser=
de.tivano.site.generic.SimpleTextParser
de.tivano.site.generic.genericTemplateFactory.cache=
de.tivano.site.generic.ProbabilisticCache
de.tivano.site.generic.genericTemplateFactory.SourceRepository=
de.tivano.site.generic.FilesystemSourceRepository
de.tivano.site.generic.genericTemplateFactory.MetaRepository=
de.tivano.site.generic.FilesystemMetaRepository
The FilesystemSourceRepository and FilesystemMetaRepository require additional
parameters, namely the filesystem paths where to look for the files:
//linebreaks for better legibility
de.tivano.site.generic.FilesystemMetaRepository.BasePath=
/path/to/metainfo
de.tivano.site.generic.FileSystemSourceRepository.BasePath=
/path/to/templates
3.2.3 Optional configuration properties
Be sure to take a look at the javadoc.
// Defines the extension for meta-information files
de.tivano.site.generic.FilesystemMetaRepository.Extension
// Defines the base path to cached files if using
// the FilesystemPersistentCache
de.tivano.site.generic.FilesystemPersistentCache.BasePath
// Defines the extension for persistent cache files
de.tivano.site.generic.FilesystemPersistentCache.Extension
// Defines the high water mark for the probailistic cache
// Refer to javadoc of PropabilisticCache
de.tivano.site.generic.ProbabilisticCache.HighWater
// Defines the high water mark for the probabilistic cache
// Refer to javadoc of PropabilisticCache
de.tivano.site.generic.ProbabilisticCache.LowWater
// Defines the class of an underlying cache.
// You might want to use the FilesystemPersistentCache
de.tivano.site.generic.ProbabilisticCache.SubCache
// When set a SiTE node is generated for several HTML-tags
// which use attributes like HREF, SRC or ACTION
// This makes URL-rewriting easy.
de.tivano.site.generic.SimpleHtmlParser.UseHrefAndSrc
// You can configure the SimpleTextParser's attribute name here.
// The default is SNAME
de.tivano.site.generic.SimpleTextParser.NameAttribute
// You can configure the SimpleTextParser's tag name here.
// The default is SITE
de.tivano.site.generic.SimpleTextParser.TagName
// Configure the syntax of ConfigurableParser and
// ConfigurableHtmlParser.
de.tivano.site.generic.ConfigurableParser.OpeningTagStart
de.tivano.site.generic.ConfigurableParser.OpeningTagEnd
de.tivano.site.generic.ConfigurableParser.ClosingTagStart
de.tivano.site.generic.ConfigurableParser.ClosingTagEnd
// When set a SiTE node is generated for several HTML-tags
// which use attributes like HREF, SRC or ACTION
// This makes URL-rewriting easy.
de.tivano.site.generic.ConfigurableHtmlParser.UseHrefAndSrc
[ t]ivano software gmbh www.tivano.de
|