cocoon-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Timothy Larson <timlarsonw...@yahoo.com>
Subject EffectTransformer/WoodyTemplateTransformer
Date Tue, 14 Oct 2003 14:44:37 GMT
I posted the source to two Java classes on my page on the wiki:
  http://wiki.cocoondev.org/Wiki.jsp?page=TimLarson
The first class, EffectTransformer, make writing transformers in Java
much easier and cleaner, IMHO.  The second class, WoodyTemplateTransformer,
is an example of its namesake rewritten to use the Effect transformer style.

Here is an explaination:

Stylesheets let the structure of your code reflect the structure of the
data you are processing, making them relatively easy to understand.
The downside to stylesheets is when you really need the support of a
full programming language and when you need side effects such as looking
up widgets or talking to a database.  Sure you can do it, but it just
does not feel right and can require too much gymnastics.

To solve this we try writing transformers in Java, but end up with the
logic for handling different elements mixed in the same startElement
method, etc. and have to introduce multiple flag and status variables
to keep track of the current state.  At least we gained a full programming
language and the ability to have side effects.

The (side-) Effect transformer solves all of these issues.

You write a transformer class which extends EffectTransformer and supplies
global data which can be directly accessed by element handler classes
implemented as inner classes.  Support is provided for the handler classes
to implement generic processing, tail recursion, and poor-man's continuations.

For each SAX event the event data is collected and the current handler is
called.  Start and end element events are handled specially.  The current
handler is told there is a nested element (event == EVENT_ELEMENT) and it is
expected to return a handler for that element.  The returned handler will
then be called with the start element event (event == EVENT_START_ELEMENT),
any nested events (EVENT_CHARACTERS, EVENT_COMMENT, etc.), and finally the
end element event (EVENT_END_ELEMENT).  At this point the enclosing handler
will automatically be reinstated from the active handler stack to receive any
further events.

Note that returning a reference to the current handler object, "this", allows
the current object to handle the nested element.

Also, returning an instance of an anonymous class which extends Handler allows
the code that handles the nested element to itself be nested inside the
current handler code.

For each event the handler can respond with either custom logic and/or use
one of the three generic processing methods:
  Returning without calling any output method discards the event.
  "out.copy()" copies the current input event to the output.
  "out.compile()" compiles the current input event.
To support custom logic, the full set of SAX events can be output using the
pattern out.methodname(parameters).

For every call, the handler is expected to return a reference to the object
that is to handle the next event.  For most events it is normal to return a
reference to the current handler object, "this".  Returning a reference to a
different object acts like tail recursion; control is passed horizontaly
to the returned object and will not return to the current handler object.

The two special cases are EVENT_ELEMENT which implements nesting of control
as explained above, and EVENT_END_ELEMENT which discards the returned value
to allow for the return of control that was nested by EVENT_ELEMENT.

Example inner handler class that assumes no errors in the input XML:

    protected class RepeaterSizeHandler extends Handler {
        public Handler process() throws SAXException {
            // The variable "event" is supplied by the EffectTransformer class
            switch(event) {
            case EVENT_START_ELEMENT:
                // Method "getRepeaterWidget" is supplied by the enclosing class
                getRepeaterWidget("RepeaterSizeHandler");
                // The variable "widget" is also supplied by the enclosing class
                ((Repeater)widget).generateSize(contentHandler);
                widget = null;
                return this;
            case EVENT_ELEMENT:
                // This is returning a built-in handler to discard nested elements
                return nullHandler;
            case EVENT_END_ELEMENT:
                return this;
            default:
                out.copy();
                return this;
            }
        }
    }

Above example modified to use poor-man's continuations to implement error checking.
The integer "step" acts as the instruction pointer that the switch statement
uses to continue right where it left off the last time the handler was called.
Any additional data that must live through a continuation should be declared
at the class level along with the variable "step".  An example of an anonymous class
is also included.

    protected class RepeaterSizeHandler extends Handler {
        protected int step = 0;
        public Handler process() throws SAXException {
            switch(step) {
            case 1:
                if (event != EVENT_START_ELEMENT) throw new SAXException("StartElement
expected!");
                getRepeaterWidget("RepeaterSizeHandler");
                ((Repeater)widget).generateSize(contentHandler);
                widget = null;
                step++;
                return this;
            case 2:
                if (event == EVENT_ELEMENT) {
                    // For good measure, here is an example of the use of an anonymous class
                    // to nest the logic for handling nested elements.
                    return new Handler() {
                        public Handler process() throws SAXException {
                            return this; 
                        }
                    }
                } else if (event == EVENT_END_ELEMENT) {
                    step++;
                    return this;
                } else {
                    out.copy();
                    return this;
                }
            default:
                throw new SAXException("I really did not expect to get called again!");
            }
        }
    }

I will give more details and examples if anybody is interested.
WDYT?

--Tim Larson


__________________________________
Do you Yahoo!?
The New Yahoo! Shopping - with improved product search
http://shopping.yahoo.com

Mime
View raw message