At present, Melati qua web application library is quite closely tied to WebMacro (although POEM is not). In future, however, we want to use JTemplater. The changeover seems a good opportunity to reassess the interaction between Melati, POEM and the templating subsystem, and to make it a little more abstract so that we can allow people to change between (say) WebMacro and JTemplater with as little fuss as possible.
Melati currently does the following jobs relating to WebMacro:
This is what MelatiServlet and its parent MelatiWMServlet do: (There is no real point to the separation; it's just that the latter is a hacked version of WMServlet.)
Optional buffering of template expansion in expand, in order to make sense of
Configurable handleException, providing by default
Automatic user login on AccessException, via a
Configurable accessHandler, which interacts with the initial
Connection to POEM Database and establishment of POEM session
These are all things we will still want to provide, but independently of WebMacro. What should MelatiServlet look like, then?
This is done between Melati and MelatiServlet.melatiContext. It's useful to structure many web applications around the notion of "the object on which the present HTTP request is operating, and the action we want to do with it".
This is what our hacked org.webmacro.engine.Variable does.
How does this relate to JTemplater? Probably we want to provide two behaviours for JTemplater, at the level of the $(...) expression in which the exception occurs: just terminate expansion and throw the exception, or interpolate a representation the exception instead of the result of the expression. But it would be nice if the latter could do some context-sensitive escaping.
$renderBody(obj.getFoo())
This is what the *MarkupLanguage classes do:
Escaping ("rendering") of text according to the conventions of the language. In fact there are several different kinds of escaping to be done in HTML, depending on the context:
Doc body, attribute value | " |
Javascript value | \042 |
URL | =22 |
Rendering of exceptions for display to the user, where the VariableExceptionHandler returns them to be displayed rather than simply throwing them out to terminate expansion. Again, this is context-dependent:
Doc body | <I>error!</I> |
Attribute value (e.g. input box content) | [error!] |
As of 21/07/2000, the representation of the exception is determined from a sub-template or "templet". The MarkupLanguage tries to load a WebMacro template (through the servlet's Broker) from a directory named after the markup language. The filename of the template is taken at first to be the package-qualified class name of the exception with .wm appended; if that doesn't exist, it tries the superclass, and so on until it finds java.lang.Exception.wm, which just does
[$ml.rendered($exception.toString())]
The templet is expanded to a string against a specially constructed Context, and the string is returned to WebMacro for interpolation into the parent template. It would obviously be nice if the templet could be expanded directly to the output stream, so that the extra buffering could be avoided.
Rendering of POEM Fields, i.e. values wrapped up with PoemTypes, for display. (N.B. do we need a PoemTypedValue which is like a Field but doesn't have a name? The name isn't needed here, only for rendering input controls.)
The functionality is: compute the field's localised cooked string representation; size-limit it; escape it for the doc-body context.
Exceptions are caught and passed to VariableExceptionHandler set up in the expansion context, which can throw them out or return them to be rendered for the doc-body context as above.
The locale is set up when the MarkupLanguage is constructed: at present it's always UK.
There are different variants for calling up SHORT, MEDIUM, LONG and FULL styles. This is really intended for use on dates.
The size limit is generally 10M, and is only meant to prevent out-of-memory errors. There is a variant which limits to 50 chars, for short displays such as the admin system browse lists.
Rendering of POEM Persistents, which just means calling up their localised displayString and rendering that in the doc-body context.
Rendering of an input box, dropdown, etc. appropriate for entering the value of a POEM Field. This has always used a templet mechanism similar to that used for exceptions: a markup language-specific directory is searched for a templet named after the PoemType of the field, except that the templet name can also be set explicitly in the field's getRenderInfo property (ultimately, in the admin interface).
The name of the field is prefixed with field_ (the prefixing is done in each templet individually, which doesn't seem right). When the returning form is interpreted, Melati.extractFields knows to put that prefix on the field names of the Persistent into which things are going.
The decision as to how to render a given object is done in a strangely mixed way. rendered(Field) is chosen by WebMacro's introspection mechanism, but everything else falls into rendered(Object), where an instanceof dispatch decides what to do. Probably it's best to do everything by the former mechanism (or even better by static method selection according to signature in the case of JTemplater), and disallow the rendering of arbitrary Objects using toString.
Probably the display of everything should be delegated to markup language-specific templets, not just Exceptions and Fields.
What are we rendering and what context are we rendering in are essentially orthogonal considerations. This is actually handled quite well in the current implementation, where HTMLMarkupLanguage will give you an AttributeHTMLMarkupLanguage as one of its properties. However, the most tricky situations, such as that in which one is rendering a JavaScript expression containing a value in quotes will which eventually become part of the document body, may not be adequately covered.
The idea of making Melati essentially a utility object ($melati) accessible within templates still seems a good one. However,
It shouldn't care which templating mechanism is being used. The same abstract logic should work with WebMacro and JTemplater.
It should be capable, where possible, of rendering complex objects directly to the output stream rather than returning a string for interpolation.
It should be simpler. There are rather a lot of here's one I found handy once methods floating around.
It should make it more obvious how to create a $melati object from outside a MelatiServlet.
It should bring out more clearly the notions of expansion context and raw/cooked representation.
It should make the current object feature a bit more optional. In particular the logical database should probably be configurable in a .properties file.
It should make it easy to say make me a dropdown without having to know what a Field is.
Generalising from that last point,
It should have a three-level stack of methods for value and input-control rendering, operating at the level of the Persistent, the Field and entirely non-POEM objects. These should be isolated into separate classes so that the templets mechanism can be compiled independently of POEM.