Looks very interesting. A nice addition in place of contrib:Choose.
Why don't you put this in a JIRA issue?
--
Ing. Leonardo Quijano Vincenzi
Director Técnico
DTQ Software
Patrick Casey wrote:
*The Problem:*
Rendering a page with a large number of conditionally displayed
sub-sections inside of tapestry produces some remarkable ugly looking
code; more to the point it tends to gun up your performance because
the system is evaluating each and every one of your conditions, even
if you, as is quite common, want one and only one section to render.
e.g.
<span [EMAIL PROTECTED] condition=”ognl:today ==
[EMAIL PROTECTED] >
Welcome to work. Hope your weekend was good.
</span>
<span [EMAIL PROTECTED] condition=”ognl:today ==
[EMAIL PROTECTED] >
Welcome back.
</span>
<span [EMAIL PROTECTED] condition=”ognl:today ==
[EMAIL PROTECTED] >
Only three more days to go!
</span>
…
My recent dive into the bowels of ognl with a profiler (see my
previous post on the subject) actually inspired me to set about
minimizing the number of ognl calls my applications have to make. So,
this was my attempt to dramatically trim down the number of
@Conditionals I have to use.
*The Solution:*
I’ve implemented (and attached) a Tapestry pseudo switch … case
component pair. To use it, set up an outer switch component, and nest
as many case components within it as you want e.g.
<span jwcid=*"@Switch"* switchOn="ognl:today">
<span jwcid=*"@Case"* token="ognl:@[EMAIL PROTECTED]">
Welcome to work. Hope your weekend was good.
</span>
<span jwcid=*"@Case"* token="ognl:@[EMAIL PROTECTED]">
Welcome back.
</span>
<span jwcid=*"@Case"* token="ognl:@[EMAIL PROTECTED]">
Only three more days to go!
</span>
</span>
*Limitations:*
1) You can’t put anything inside the switch statement **except** case
statement. Anything between the start of the @Switch span and the end
of the @Switch span which isn’t an @Case, gets ignored completely.
2) The switch statement switches on a string (internally it’s just a
hashmap). So if you want to switch on something else, convert it to a
string before you pass it in.
3) I haven’t (yet at least) figured out how to support multiple cases
resolving into the same execution block. So if you want Tuesday,
Wednesday, and Thursday all to say “The middle of the week sucks”,
you’ll need to have three case blocks, one for each day L. Maybe
somebody smarter than I can sort out how to stack them.
*Performance Notes:*
1) Yes, it is faster than either @Conditionals or @contrib:Choose.
Profiled render time for 5 renders of one of my more complex forms was
3.520 seconds for @Choose, 3.323 seconds for @Conditional and 2.523
seconds for @Switch. The speed comes from reducing the number of
(hugely) expensive ognl calls it has to make, largely by reducing the
size of the expressions e.g. “ognl:today ==
[EMAIL PROTECTED] <mailto:[EMAIL PROTECTED]>”
becomes “ognl: @[EMAIL PROTECTED], halving the number of
evaluation cycles. Curiously, contrib:Choose is actually marginally
slower according to my profiling than just brute force @Conditionals.
I’ve chalked it up as random variance in my profiling for the moment,
but its still a head scratcher.
2) I have to evaluate each @Case token to determine if it’s a match
for the switch, so if you can use a **static** @Case statement,
you’ll, again, save yourself an ognl call thus token=”4” is
dramatically faster than token=”ognl:getFour()”. Sometimes that’s not
possible, of course, so I still support the ognl.
*General Case Caveot:*
As usual, whenever I’m mucking around in the bowels of Tapestry, I’m
reminding that I don’t think the same way Howard does; so there’s a
small but very real chance that I built this whole component based on
some deeply flawed and fundamentally dangerous assumptions about the
framework. With that said though, it **does** work for me, and I’m
planning on rolling it out as part of the current project I’m working on.
------------------------------------------------------------------------
package components;
import org.apache.tapestry.AbstractComponent;
import org.apache.tapestry.IMarkupWriter;
import org.apache.tapestry.IRequestCycle;
public abstract class Case extends AbstractComponent {
private String fToken;
protected void renderComponent(IMarkupWriter writer, IRequestCycle
cycle) {
}
public String getToken() {
return fToken;
}
public void setToken(String token) {
fToken = token;
}
public abstract String getElement();
}
------------------------------------------------------------------------
package components;
import java.util.HashMap;
import org.apache.tapestry.AbstractComponent;
import org.apache.tapestry.IBinding;
import org.apache.tapestry.IMarkupWriter;
import org.apache.tapestry.IRender;
import org.apache.tapestry.IRequestCycle;
public abstract class Switch extends AbstractComponent {
private String fSwitchOn;
private boolean initialized = false;
private HashMap fMap;
public String getSwitchOn() {
return fSwitchOn;
}
public void setSwitchOn(String switchOn) {
fSwitchOn = switchOn;
}
private void initialize() {
if (initialized)
return;
fMap = new HashMap();
IRender[] foo = this.getBody();
for (int x=0;x<foo.length; x++) {
IRender one = foo[x];
if (one instanceof Case) {
Case ca = (Case) one;
IBinding bind = ca.getBinding("token");
String value = bind.getString();
fMap.put(value, one);
}
}
initialized = true;
}
@Override
protected void renderComponent(IMarkupWriter writer, IRequestCycle
cycle) {
initialize();
Case block = (Case) fMap.get(fSwitchOn);
if (block != null)
block.renderBody(writer,cycle);
}
public abstract String getElement();
}
------------------------------------------------------------------------
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]