JPT: User's Guide
last modified: 2004/05/12
author Chris Rossi
 
Zope Page Templates

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.

TALES Expression Syntax

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.

Path Expressions

The first element in a path expression must be a variable, a class, or a literal.

Literals

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:

Variables

A variable is either predefined, defined via a tal:define attribute, or passed in to the template at runtime. The following variables are predefined:

The following variables are defined in ZPT but not in JPT: options, 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.

Classes

A class may be referred to by it's fully qualified name. This can allow calling static methods on the class. Example:

A fully qualifed class name followed by .class allows you to refer to the class object itself. Example:

Path traversal

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:

Alternatively, if a parent object is a Map it will be treated as a dictionary and the processor will attempt a lookup with the next path element as key.

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.

Properties and methods must be publically accessible. The last element in a path expression may resolve to null, but if an intermediate element resolves to null a NoSuchPathException will be thrown.

Arrays

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.

Helper Objects

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.

BoolHelper

The built-in variable bool is a helper object with the following static methods:

An example:

MathHelper

The built-in variable math is a helper object with the following static methods:

In all cases, x and y are assumed integers. Some examples:

DateHelper

The built-in variable date is a helper object with the following static methods:

The format string follows the same rules used by
SimpleDateFormat. An example:

Java expressions

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:

Scripts

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

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 expressions

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

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:

Other expressions

String expressions behave exactly as in ZPT. Python and Nocall expressions are not supported in JPT.

TAL Statements

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.

tal:repeat

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

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,

The 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>

Invoking Page Templates

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);
        }
    }
}