JPT is a Java implementation of Zope Page Templates (ZPT). Because JPT isn't running in the context of Zope and isn't written with Python, there are necessarily some differences between JPT and ZPT. This document will concentrate on the ways that JPT differs from ZPT. For an introduction to ZPT refer to the chapter Using Zope Page Templates in the Zope Book. For a complete reference to ZPT, refer to the ZPT Reference.
There are some fundamental differences between path expressions in ZPT and JPT. Generally speaking, path expressions are resolved in JPT by traversing, by means of reflection, properties and methods of Java objects.
The first element in a path expression must be a variable, a class, or a literal.
Numeric and boolean literals are defined in the same way as in the Java language. String literals are delimited by single quotes. Some example literals:
A variable is either predefined, defined
via a tal:define attribute, or passed in to the template at runtime.
The following variables are predefined:
here refers to the context object, passed in at runtimetemplate refers to the template objectresolver refers to an instance of Resolver and is
used to find resources, such as other templates. see metal:use-macrorepeat see tal:repeatbool an instance of a bool helpermath an instance of a math helperdate an instance of a date helperoptions, CONTEXTS, root, container, request, user, modules.
The following variables are defined in ZPT but aren't yet implemented in JPT:
nothing, default, attrs. If you need these, holler.
A class may be referred to by it's fully qualified name. This can allow calling static methods on the class. Example:
java.lang.System.currentTimeMillis().class allows you to
refer to the class object itself. Example:
java.lang.Integer.class/instanceof( here/number )Following the initial path element, path elements are either properties or methods of the preceding object. Properties are denoted as a simple name and are resolved as Java Bean properties. Example:
here/name will search for a method getName()
in the passed context object.
Map it will be treated as
a dictionary and the processor will attempt a lookup with the next path element
as key.
here/people/suzanne if people is an instance
of Map, expression will be equivalent to the Java code:
people.get( "suzanne" )An arbitrary method may be called by specifying the method name followed by an argument list contained in parentheses. In the preceding example, 'here/name' is equivalent to 'here/getName()'. Arguments in method calls are evaluated as expressions.
null, but if an intermediate element resolves
to null a NoSuchPathException will be thrown.
Array members may be accessed using the same syntax as in Java. Any number of dimensions are supported. The expression inside the array accessor may be any TALES expression and must evaluate to an integer value. If an array accessor is found modifying an object that is not an array an exception is thrown.
here/people[2]here/grid[point/x][point/y]DEPRECATED: Helper objects were added to JPT before the author discovered BeanShell and integrated it into JPT. This allows JPT the use of Java expressions just like the use of Python expressions in ZPT. Helper objects are still included for backwards compatability although they will probably be phased out at some later date. For the most part there's no reason to use them instead of a Java expression, with the possible exception of the DateHelper which allows formatting a date in one step, rather than having to instantiate a SimpleDateFormat and then call it.
In ZPT, when expressions start to get complicated, you can resort to using
python expressions. Although, truly complicated logic should be encapsulated
in methods in external objects, sometimes you just want write a simple
boolean expression or add two numbers. Since python expressions are not
available to JPT, JPT includes a handful of helper objects, math,
bool and date which are available to the template as variables.
The built-in variable bool is a helper object with the following
static methods:
or( x, y ) boolean or of expressions x and yand( x, y ) boolean and of expressions x and y<p tal:condition="bool/and( exists:here/pets/dog, not:here/pets/dog/badDog )">
Good Dog<p>
The built-in variable date is a helper object with the following
static methods:
format( formatString, date ) format date according to format stringdate/format( 'EEE MMMM d, yyyy h:mm:ss a', here/birthday )
Java expressions are made possible in JPT by BeanShell,
a scripting language that is fully compatible with the Java programming language and adds
some features of its own. For the complete rundown and what you can do with BeanShell,
you should visit their website. The interpreter
is very light weight and makes some very cool things possible. JPT is distributed with
the bsh-core library jar which contains just the bare minimum functionality
to evaluate Java expressions. To use some of the more advanced BeanShell features just
to to their website and download the complete
bsh jar or the specific add-on jar that you're interested
in and put it in your classpath.
Java expressions work just like Python expressions in ZPT except that the Java language is used instead of Python. Any legal Java (or BeanShell) expression may be evaluated. Some examples:
java: 4 + 5 returns the int 9java: random = new Random( System.currentTimeMillis() ); 1 + random.nextInt( 10 )
generates a random number between 1 and 10.
Snippets of BeanShell code that are longer than a line or two can be stored externally
in a script that is callable by JPT. Scripts are accessed using the resolver
variable just like with macros. The
Resolver.getBeanShellScript( String uri ) method returns a BeanShell script
object that is evaluated by executing the script. The return value, if not specified by
a return statement is that last expression in the script.
Example:
<div tal:content="resolver/getBeanShellScript( 'coolscript.bsh' )">
The output of my very cool BeanShell script.
</div>
Variables are shared between the page template and the BeanShell context. So all of the variables already defined for the template are available in Java expressions or scripts. And by the same token, any new variable defined in a script or Java expression can be accessed from a path expression.
Exists epxressions work more or less like in ZPT. If an expression evaluates to
null or causes a NoSuchPathException to be thrown, the expression
evaluates to false. Otherwise the expression evaluates to true.
Not expressions work more or less like in ZPT. The expression to which not:
is applied must first be cast to a boolean. The result is then negated. Casts to
boolean follow these rules:
false; any non-zero number evaluates to
true.false; those containing any
non-zero number of elements evaluate to truenull evaluates to false. Any non-null object evaluates
to true, unless that object is an instance of Boolean,
Number, Collection, or Map, in which case
it evaluates according to the rules above.String expressions behave exactly as in ZPT. Python and Nocall expressions are not supported in JPT.
All TAL statements behave almost exactly as in ZPT, exept for tal:no-call which
is not yet implemented. (If you need it, holler.) jpt:define and
jpt:omit-tag must cast their expression to a boolean, which follows the rules
described for Not expressions.
There are a few minor variations for tal:repeat. The repeat expression must
evaluate to an array, an Iterator or a Collection. If the
expression evaluates
to an Iterator the repeat variable length is undefined. The
repeat variables Letter and Roman have been changed in JPT to
capitalLetter and capitalRoman, to avoid confusion with Java
case conventions.
METAL statements behave exactly as in ZPT. The only difference, which is really a difference
in Path expressions, is the means of finding another template which contains macros. Since,
there is no Zope tree in which to locate templates, templates must be resolved via a URI.
The predefined variable, resolver, holds a reference to an instance of
Resolver which can be used to resolve references to external templates.
The Resolver class contains two methods,
getPageTemplate method returns a PageTemplate instance which can then
be used for macro processing. getBeanShellScript returns
BeanShell script that is then executed.
getResource is there in case anyone needs a more generic resource
finding method and returns a URL. If the URI passed to these methods is relative
it will be resolved relative to the URI of the current template.
For example:
<html metal:use-macro="resolver/getPageTemplate( '../base.jpt' )/macros/page">
<div metal:fill-slot="content">
This is the content for my web page.
</div>
</html>
PageTemplate has been made into an interface, in case anyone wants to
write a different implementation. To date the only known implementation is the
PageTemplateImpl included in this package. The PageTemplate interface looks like
this:
package com.webslingerZ.jpt;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Map;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.ext.LexicalHandler;
public interface PageTemplate {
static final String TAL_NAMESPACE_URI = "http://xml.zope.org/namespaces/tal";
static final String METAL_NAMESPACE_URI = "http://xml.zope.org/namespaces/metal";
void process( ContentHandler contentHandler, LexicalHandler lexicalHandler, Object context, Map dictionary )
throws SAXException, PageTemplateException, IOException;
void process( OutputStream output, Object context )
throws SAXException, PageTemplateException, IOException;
void process( OutputStream output, Object context, Map dictionary )
throws SAXException, PageTemplateException, IOException;
Map getMacros();
}
The context may be any Java object. This is the same object referred to by here
in path expressions. The dictionary is optional and may be used to define variables for use
by the template. You have the option of passing in an OutputStream, in which case the result
of the template processing will be serialized directly to that stream, or specifying SAX handlers in
which case the results of template processing will be streamed as SAX events to those handlers.
An example of invoking a PageTemplate from a Servlet:
public class MyServlet extends HttpServlet {
public void service( HttpServletRequest request, HttpServletResponse response )
throws ServletException, IOException
{
// Some business logic here. . . .
User user = . . .
Record record = . . .
// Response to user
try {
// Find and instantiate template, located in the record directory
// under our web application root.
URL templateURL = getServletContext().getResource( "record/showrecord.jpt" );
PageTemplate template = new PageTemplateImpl( templateURL );
// Initialize some variables to be used by the template
Map dictionary = new HashMap();
dictionary.put( "request", request );
dictionary.put( "user", user );
// Output response
OutputStream output = response.getOutputStream();
response.setContentType( "text/html" );
template.process( output, record, dictionary );
output.close();
} catch( PageTemplateException e ) {
// Oh no!
throw new ServletException(e);
}
}
}