You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@cocoon.apache.org by Jonas Ekstedt <ek...@ibg.uu.se> on 2004/12/05 14:09:09 UTC
Implementation of a tag attribute language
Hello
The recent discussions about tag attribute languages has been very
interesting and enlightening. To make it more concrete here are a few
ideas on how to actually implement these concepts.
TALGenerator
------------
* Extends the generator in the template block.
* Does not support tag bundles (ie taglibs)
* Only treats elements containing a tal:do attribute in a special way.
A new TALToken is introduced (for an introduction to tokens see
http://www.mail-archive.com/dev@cocoon.apache.org/msg25040.html). Here's
an example of how a TAL template is compiled:
<table>
<tr tal:do="forEach(listOfBeans)">
<td>${it.beanProperty1}</td>
<td>${it.beanProperty2}</td>
</tr>
</table>
=> (ignoring whitespace nodes) =>
PlainElementToken localName="table"
TALToken localName="tr", do="forEach(listOfBeans)"
PlainElementToken localName="td"
ExpressionToken expression="it.beanProperty1"
PlainElementToken localName="td"
ExpressionToken expression="it.beanProperty2"
TALToken might look something like:
public class TALToken {
...
public void invoke(ScriptContext context) {
if (getDoCommand().equals("forEach")) {
Iterator it = evaluateToIterator(getDoParameter(0));
while (it.hasNext()) {
context.getVariables().put("it", it.next());
context.getConsumer().startElement(uri, localName, qname, attrs)
invokeBody(context);
context.getConsumer().endElement(uri, localName, qname);
}
} else if (getDoCommand().equals("if")) {
boolean test = evaluateToBoolean(getDoParameter(0));
if (test) {
context.getConsumer().startElement(uri, localName, qname, attrs)
invokeBody(context);
context.getConsumer().endElement(uri, localName, qname);
}
} else if (...) {
...
}
}
}
I think a TALGenerator that is limited in scope (ie only supports a
few control structures like if, forEach etc) would be quite easy
to implement if it is built on top of the current template block.
Cheers Jonas
Re: Implementation of a tag attribute language
Posted by Daniel Fagerstrom <da...@nada.kth.se>.
Jonas Ekstedt wrote:
> On Sun, 5 Dec 2004, Daniel Fagerstrom wrote:
<snip/>
> Yeah, that's a better idea. We could even go a step further:
>
> PlainElementToken localName="table"
> ForEachTALToken parameter[0]="listOfBeans"
> IfTALToken parameter[0]="a < b"
> PlainElementToken localName="tr"
> ...
Yes.
<snip/>
>>By having a standardized way to pass arguments to both attribute and tag
>>driven directives they can share implementation.
>
> I'd say they're sufficiently different to warrant separate
> implementations.
We'll see no need to decide now.
> A TALToken parameter can be stored
> as a single Expression object whereas a TagToken attribute is either a
> CharactersToken or an ExpressionToken (or even a mix of both).
Yes, you're right. I don't know, but I would suggest that it is an
mistake in the implementation of JXTG. Many of JXTGs tags are modelled
after XSLT and XSLT let the "xsl:" tags "decide" how an attribute should
be interpreted. I would prefer the same strategy for tags in Template.
But as we strive for back compabillity I guess it is not possible.
> There are
> probably other differences as well. Also there won't be all that many
> TALTokens so the code duplication will be negligble.
Yes.
<snip/>
>>WDYT, am I missing something? Do you have anything against if I refactor
>>the code in the direction I described above?
>
> What you propose is certainly a better solution. I always felt
> uneased by getStart(), getStartBody() and the likes. Won't be able to help
> unfortunately as exams are looming.
Good, then I take care of the refactoring.
/Daniel
Re: Implementation of a tag attribute language
Posted by Jonas Ekstedt <ek...@ibg.uu.se>.
On Sun, 5 Dec 2004, Daniel Fagerstrom wrote:
snip...
> > <table>
> > <tr tal:do="forEach(listOfBeans)">
> > <td>${it.beanProperty1}</td>
> > <td>${it.beanProperty2}</td>
> > </tr>
> > </table>
> >
> > => (ignoring whitespace nodes) =>
> >
> > PlainElementToken localName="table"
> > TALToken localName="tr", do="forEach(listOfBeans)"
> > PlainElementToken localName="td"
> > ExpressionToken expression="it.beanProperty1"
> > PlainElementToken localName="td"
> > ExpressionToken expression="it.beanProperty2"
>
> Something in that direction. I would prefer this:
>
> PlainElementToken localName="table"
> TALToken do="forEach(listOfBeans)"
> PlainElementToken localName="tr"
> PlainElementToken localName="td"
> ExpressionToken expression="it.beanProperty1"
> PlainElementToken localName="td"
> ExpressionToken expression="it.beanProperty2"
>
> It gives the TALToken access to its enclosing element (with the tal:do
> attribute removed). It also generalizes to multiple directives in an
> natural way:
>
> <tr tal:do="forEach(listOfBeans);if(...)">
> ...
>
> =>
>
> PlainElementToken localName="table"
> TALToken do="forEach(listOfBeans)"
> TALToken do="if(...)"
> PlainElementToken localName="tr"
> ...
>
> Where the "forEach" TALToken invoke the "if" TALToken while executing
> invokeBody, which in turn can invoke the "tr" PlainElementToken. The
> "getAttributes" method in the TALToken class just calls the
> "getAttributes" method in its child.
Yeah, that's a better idea. We could even go a step further:
PlainElementToken localName="table"
ForEachTALToken parameter[0]="listOfBeans"
IfTALToken parameter[0]="a < b"
PlainElementToken localName="tr"
...
It shouldn't however, as you say, be possible to add custom TALTokens.
snip...
> By having a standardized way to pass arguments to both attribute and tag
> driven directives they can share implementation.
I'd say they're sufficiently different to warrant separate
implementations. A TALToken parameter can be stored
as a single Expression object whereas a TagToken attribute is either a
CharactersToken or an ExpressionToken (or even a mix of both). There are
probably other differences as well. Also there won't be all that many
TALTokens so the code duplication will be negligble.
snip...
> BTW: I think that your implementation of the script and token structure
> is quite cool in the way that we get the tree structure nearly for free
> and it also seem quite space efficient. But on the other hand I believe
> that the explicit handling data structure implementation details spreads
> to much to other parts of the code. Which will make the implementation
> unecesarilly complex.
>
> I would prefer to refactor the code so that element, attribute and
> character tokens are responsible for their children with methods like
> "addChild(...)", "addAttribute(...)", "getAttributeItterator()" etc.
> This can be implemented efficiently by e.g. using single linked lists of
> tokens where each token has a reference to the next token.
>
> We can squeze out the last few bits of space efficency later if needed,
> but at this stage in the project I think we should strive for simplicity.
>
> WDYT, am I missing something? Do you have anything against if I refactor
> the code in the direction I described above?
What you propose is certainly a better solution. I always felt
uneased by getStart(), getStartBody() and the likes. Won't be able to help
unfortunately as exams are looming.
Cheers Jonas
Re: Implementation of a tag attribute language
Posted by Daniel Fagerstrom <da...@nada.kth.se>.
Jonas Ekstedt wrote:
> Hello
>
> The recent discussions about tag attribute languages has been very
> interesting and enlightening.
Both by generating interesting ideas and illustraiting some interesting
aspects of mail list social dynamics ;)
> To make it more concrete here are a few
> ideas on how to actually implement these concepts.
>
> TALGenerator
> ------------
> * Extends the generator in the template block.
> * Does not support tag bundles (ie taglibs)
> * Only treats elements containing a tal:do attribute in a special way.
>
> A new TALToken is introduced (for an introduction to tokens see
> http://www.mail-archive.com/dev@cocoon.apache.org/msg25040.html). Here's
> an example of how a TAL template is compiled:
>
> <table>
> <tr tal:do="forEach(listOfBeans)">
> <td>${it.beanProperty1}</td>
> <td>${it.beanProperty2}</td>
> </tr>
> </table>
>
> => (ignoring whitespace nodes) =>
>
> PlainElementToken localName="table"
> TALToken localName="tr", do="forEach(listOfBeans)"
> PlainElementToken localName="td"
> ExpressionToken expression="it.beanProperty1"
> PlainElementToken localName="td"
> ExpressionToken expression="it.beanProperty2"
Something in that direction. I would prefer this:
PlainElementToken localName="table"
TALToken do="forEach(listOfBeans)"
PlainElementToken localName="tr"
PlainElementToken localName="td"
ExpressionToken expression="it.beanProperty1"
PlainElementToken localName="td"
ExpressionToken expression="it.beanProperty2"
It gives the TALToken access to its enclosing element (with the tal:do
attribute removed). It also generalizes to multiple directives in an
natural way:
<tr tal:do="forEach(listOfBeans);if(...)">
...
=>
PlainElementToken localName="table"
TALToken do="forEach(listOfBeans)"
TALToken do="if(...)"
PlainElementToken localName="tr"
...
Where the "forEach" TALToken invoke the "if" TALToken while executing
invokeBody, which in turn can invoke the "tr" PlainElementToken. The
"getAttributes" method in the TALToken class just calls the
"getAttributes" method in its child.
>
> TALToken might look something like:
>
> public class TALToken {
> ...
>
> public void invoke(ScriptContext context) {
> if (getDoCommand().equals("forEach")) {
>
> Iterator it = evaluateToIterator(getDoParameter(0));
> while (it.hasNext()) {
> context.getVariables().put("it", it.next());
> context.getConsumer().startElement(uri, localName, qname, attrs)
> invokeBody(context);
> context.getConsumer().endElement(uri, localName, qname);
> }
>
> } else if (getDoCommand().equals("if")) {
>
> boolean test = evaluateToBoolean(getDoParameter(0));
> if (test) {
> context.getConsumer().startElement(uri, localName, qname, attrs)
> invokeBody(context);
> context.getConsumer().endElement(uri, localName, qname);
> }
>
> } else if (...) {
> ...
> }
> }
> }
Yes, that will do it, but by separating the TAL handling and element
handling we get rid of the calls to "startElement" and "endElement",
that is done in the PlainElementToken handling.
By having a standardized way to pass arguments to both attribute and tag
driven directives they can share implementation.
The directives should still IMO be implemented as separate classes. Then
we can install them programatically in the TalTemplateGenerator and
declare the class as final. In that way we protect the users when they
become obsessed by self destructive thoughts and want to add their own
directives ;)
> I think a TALGenerator that is limited in scope (ie only supports a
> few control structures like if, forEach etc) would be quite easy
> to implement if it is built on top of the current template block.
Certainly.
Do you have any suggestions about how to implement the directives parser?
BTW: I think that your implementation of the script and token structure
is quite cool in the way that we get the tree structure nearly for free
and it also seem quite space efficient. But on the other hand I believe
that the explicit handling data structure implementation details spreads
to much to other parts of the code. Which will make the implementation
unecesarilly complex.
I would prefer to refactor the code so that element, attribute and
character tokens are responsible for their children with methods like
"addChild(...)", "addAttribute(...)", "getAttributeItterator()" etc.
This can be implemented efficiently by e.g. using single linked lists of
tokens where each token has a reference to the next token.
We can squeze out the last few bits of space efficency later if needed,
but at this stage in the project I think we should strive for simplicity.
WDYT, am I missing something? Do you have anything against if I refactor
the code in the direction I described above?
/Daniel