I have done some progress in refactoring JXTG and would like to summarize the current state and direction for those interested. I'm not repeating things that are actively discussed in other threads.

Goal
====

The goal with the refactoring is to factor out things that are useful in other contexts from JXTG and to make the code easier to understand and maintain.

More specifically:

* Factor out expression handling
* Factor out the object model
* Factor out "string template" handling, i.e. handling of strings in attributes and elements that contain expressions
* Factor out instructions
* Factor out duplicated code


What is left after this is a reusable template engine and a number of plugable components that can be used in it. I will be happy with the state of the refactoring when an attribute template language (http://wiki.apache.org/cocoon/Templates) can be implemented in terms of the template engine and some of the components.

As suggested by the term "refactoring", JXTG is supposed to stay back compatible. But as can be seen in some other threads there are some "weird" behaviours in current JXTG that we should do something about in one way or another.

State/Roadmap
=============

Expression Handling
-------------------

All Jexl and JXPath code is factored out to a pluggable expression package o.a.c.components.expression.* and is described in http://marc.theaimsgroup.com/?t=110595829700001&r=1&w=2. When it is polished enough I hope it can be part of Cocoon core so that we can get expression handling that behave in the same way everywhere in Cocoon.

The only class in JXTG that know anything about Expression is o.a.c.template.jxtg.expression.JXTExpression. That embeds the Expression, has factory methods for creating expressions and access methods that do type conversion.

I think we can keep JXTExpression about as is for the time being. But we should make the expression compilation more pluggable, now it just follows JXTGs model. Also the convertions should preferably be done in an own component, but that can wait.

Object Model
------------

The object model is factored out to o.a.c.environment.FlowObjectModelHelper that is a copy of Carsten's o.a.c.environment.TemplateObjectModelHelper from scratchpad, with some small modifications. Especially there is a factory method that creates an ExpressionContext with the object model as content. Also here I would like to move this class to core when it is pollished enough. So that we can reuse the object model in other places. One interesting application a "generic" FOM expression module that could be used instead of all other input modules and that would make Cocoon more coherent is discussed in http://marc.theaimsgroup.com/?t=110633024300002&r=1&w=2.

From JXTG POV I think it is ok except for more extensive testing and possible debugging.

String Template
---------------

The "string template" is mainly gathered in o.a.c.template.jxtg.expression.Substitutions which contain a list of Subst that either are JXTExpression or Literal. Here there is some work left to do. I would like the string parsing to be plugable so that one can use other schemes than the ${} and #{} from JXTG. Also some code in the Invoker for evaluating a Substitution to a string could be moved to Substitution.

Instructions
------------

The execution engine consists of o.a.c.template.jxtg.script.ScriptManager that take care of compiling and caching the script. o.a.c.template.jxtg.script.Parser that does the compilation, o.a.c.template.jxtg.script.event.* that are used to store SAX events and o.a.c.template.jxtg.instruction.* that are the actual JXTG instructions.

The goal is that only instruction.* should be JXTG specific and that everything under jxtg.script should be pluggable wrt instructions, expressions and string parser and moved to o.a.c.template.script. We are not there yet.

Construction
- - - - - -

Starting with the instructions they are supposed to be thread safe. This is partly enforced by having all member variables final and use the constructor for initializing everything that is known at compile time. The current "interface" for the constructor is:

Start<Instruction>(StartElement raw, Attributes attrs, Stack stack)
        throws SAXException;

Where the instructions extends StartInstruction and raw is the start element for the instruction jx:if etc, this is used for taking care of location etc. attrs is not suprisingly the attributes of the instruction. Stack is a stack of the enclosing elements, this is used by e.g. jx:when and jx:otherwise for installing themselves in the enclosing jx:choose.

The remaining problem for removing references to the JXTG instructions from the Parser is that StartDefine have a finish method that is executed when its end tag is reached during compilation. finish() take care of installing the parameters within jx:definition.

There are two possibilities for making StartDefine being compiled in the same way as the other instructions.

1. Let the parameters install them selves in StartDefine as when and otherwise do for choose.

2. Refactor the instructions so that they are constructed at the end tag instead of the start tag. This require more work as the instruction extends the start tag and must be made an own object that is refered to from the start tag instead. Also instead of that tags has access to the stack of parents it would have access to its compiled children.

I would prefer 2. even if it requires more work as it among other things means that all member variables can be final as everything is constructed as once.

Once this is solved we can refactor the Parser so that it take a exprssion factory, a string template parser and a set of instuctions as arguments and remove all the references to specific instructions.

Execution
- - - - -

Each instruction has an execute method that is called in the Invoker:

public Event execute(final XMLConsumer consumer,
                     ExpressionContext expressionContext,
                     ExecutionContext executionContext,
                     StartElement macroCall,
                     Event startEvent, Event endEvent)
    throws SAXException;

Here consumer, expressionContext and executionContext is rather obvious. Of the remaining arguments we have macroCall that is used to pass the macro call body that can be executed whithin a macro with jx:evalBody. For getting things simpler I would suggest that parameter to be removed and passed in the expressionContext instead, this is already done for an ordinary macro call that puts the information in the variable macro. This should be done for jx:eval as well.

One could refactor the events so that we get a more tree based design as in Jonas proposal. it is easier to write code for that than the startEvent, endEvent representation in JXTG. But I don't know if it is worthwhile.

We could simplify the invoker by puting the sax generating code in execute methods in the events also, but thats mainly cosmetic.

I would like to break out the macro execution code from the Invoker but it needs further research to see if it is possible.

Error Handling
- - - - - - -

All the instructions contains nearly identical exception handling, could we move it to the Invoker?

Import
- - -

The import instruction performes the import in during execution I would much prefer to do it during compile time. Is that possible without breaking current functionality?

Weird Instructions
- - - - - - - - -

Some of the instructions e.g. jx:set and jx:eval has somewhat problematic behaviour. This is discussed in some other threads.

                 --- o0o ---

I have most certainly forgot lots of important stuff. But this should be enough for now. Just ask about unclear things, or even better find a good answer and write about it or implement it ;)

/Daniel

Reply via email to