Hi All, Now that the release is out the door, I'd like to re-raise the extended selector proposal I brought up with Sylvain a few weeks back. It's in Bugzilla under #7244, and the initial email I sent out is attached below.
Any thoughts, comments, questions ? Cheers, Marcus On Fri, Mar 15, 2002 at 01:33:01PM +0100, Marcus Crafter wrote: > Hi All, > > Hope all is well. > > Sylvain and I have recently had a discussion about the current > selector implementation. Here's the first email from the > conversation: > > -------------------------------------------------------------------------------- > > Hi Sylvain! > > Hope all is well mate. > > I remember a while back (>6 months) on cocoon-dev we discussed the > semantics of map:select and it's current implementation. > > I remember you comments were similar to mine, and I'm > trying to catch up with where this discussion went (if > anywhere), as I've been off in project land for the past few > months. > > My current (perhaps naive) opinion is that map:select is implemented > wrong, and causes confusion among new cocoon application developers. > > The current implementation converts: > > <map:select type="myselector"> > <map:when test="this">...</map:when> > <map:when test="that">...</map:when> > </map:select> > > to: > > if (myselector.select("this"...)) { > ... > } else if (myselector.select("that"...)) { > ... > } > > This seems to be contrary to the semantics the xml implies (and > can cause unwanted behaviour) : > > I think many might expect generated java code to behave like: > > Object result = myselector.select(...); > > if (result.equals("this")) { > ... > } else if (result.equals("that")) { > ... > } > > more like a expanded switch statement. > > ie. the test case is done only once, and its return value is > checked multiple times. Not only is this better performance > wise, but prevents unwanted behaviour. eg: > > I found this out when writing a login selector. > > I had something like: > > <map:select type="login"> > <map:when test="permitted">...</> > <map:when test="denied">...</> > <map:otherwise>...</> > </map:select> > > which in practice tried to log denied users in 2 times! > > This means one has to do something like: > > <map:act type="login"/> > <map:select type="userstatus"> > <map:when test="permitted">...</> > <map:when test="denied">...</> > <map:otherwise>...</> > </map:select>> > </map:act> > > ie. combination of action and then selector, which I find to be > a hack. > > What do you think ? Has this already been covered on cocoon-dev in > recent times ? > > Cheers, > > Marcus > > -------------------------------------------------------------------------------- > > After some thinking we came up with an idea to create > an extended selector interface which supports switch-style semantics > rather than if-if-else semantics. > > The newer interface supports switch-style semantics by allowing > a developer to create a 'testable' context before any of the > <map:when> tests have been done. This context can then be used in > select() to see if the current test yield a true value. > > The newer interface looks like: > > public interface ContextualizableSelector extends Selector, ThreadSafe > { > Object getSelectorContext(Map objectModel, Parameters parameters); > boolean select(String expression, Object selectorContext); > } > > There's an Abstract class to support the older style: > > public abstract class AbstractContextualizableSelector extends AbstractLoggable > implements ContextualizableSelector > { > public boolean select( > String expr, Map objectModel, Parameters params > ) > { > return select(expr, getSelectorContext(objectModel, params)); > } > } > > A sample implementation would be like: > > public class RequestParameterSelector extends AbstractContextualizableSelector > implements Configurable > { > public Object getSelectorContext(Map objectModel, Parameters params) > { > <snip> > String name = params.getParameter("parameter-name", this.defaultName); > return ObjectModelHelper.getRequest(objectModel).getParameter(name); > } > > public boolean select(String expression, Object selectorContext) > { > return selectorContext.equals(expression); > } > } > > Ok, it's a simple example, but for selectors that calculate more > complex conditionals, or do some thing like attempt to log in a > user for example, the getSelectorContext(...) would give them a > huge saving. > > The current files use Contextualizable as the prefix which we're > not so happy with as a name as it might confuse people with > avalon's context or the cocoon environment context. If anyone has a > better idea please let us know. So far the only alternative name has > been SwitchStyleSelector (?) > > I've put together some diffs which are attached (updated > sitemap.xsl, interfaces and abstract class, and a modified > RequestParameterSelector as a sample). We'd be interested in your > comments. > > Cheers, > > Marcus > > -- > ..... > ,,$$$$$$$$$, Marcus Crafter > ;$' '$$$$: Computer Systems Engineer > $: $$$$: ManageSoft GmbH > $ o_)$$$: 82-84 Mainzer Landstrasse > ;$, _/\ &&:' 60327 Frankfurt Germany > ' /( &&& > \_&&&&' > &&&&. > &&&&&&&: > ? src/java/org/apache/cocoon/selection/AbstractContextualizableSelector.java > ? src/java/org/apache/cocoon/selection/ContextualizableSelector.java > Index: src/java/org/apache/cocoon/components/language/markup/sitemap/java/sitemap.xsl > =================================================================== > RCS file: >/home/cvspublic/xml-cocoon2/src/java/org/apache/cocoon/components/language/markup/sitemap/java/sitemap.xsl,v > retrieving revision 1.10 > diff -u -r1.10 sitemap.xsl > --- src/java/org/apache/cocoon/components/language/markup/sitemap/java/sitemap.xsl > 1 Mar 2002 15:09:04 -0000 1.10 > +++ src/java/org/apache/cocoon/components/language/markup/sitemap/java/sitemap.xsl > 15 Mar 2002 11:36:14 -0000 > @@ -193,6 +193,7 @@ > import org.apache.cocoon.matching.Matcher; > import org.apache.cocoon.matching.PreparableMatcher; > import org.apache.cocoon.selection.Selector; > + import org.apache.cocoon.selection.ContextualizableSelector; > import org.apache.cocoon.sitemap.AbstractSitemap; > import org.apache.cocoon.components.pipeline.StreamPipeline; > import org.apache.cocoon.components.pipeline.EventPipeline; > @@ -242,16 +243,36 @@ > /** > * Method that handles selectors. > */ > - private boolean isSelected(String hint, String testValue, Parameters params, >Map objectModel) throws Exception { > - Selector selector = (Selector)this.selectors.select(hint); > + private boolean isSelected(String hint, String testValue, Parameters params, >Map objectModel, Object selectorContext) throws Exception { > + Component selector = (Selector)this.selectors.select(hint); > try { > - return selector.select(testValue, objectModel, params); > + if (selector instanceof ContextualizableSelector) > + return ((ContextualizableSelector) selector).select( > + testValue, selectorContext > + ); > + else // support normal selector > + return ((Selector) selector).select(testValue, objectModel, params); > } finally { > this.selectors.release(selector); > } > } > > /** > + * Method that handles selectors. > + */ > + private Object getSelectorContext(String hint, Map objectModel, Parameters >params) throws Exception { > + Component selector = this.selectors.select(hint); > + try { > + if (selector instanceof ContextualizableSelector) > + return ((ContextualizableSelector) >selector).getSelectorContext(objectModel, params); > + } finally { > + this.selectors.release(selector); > + } > + // non-ContextualizableSelector > + return null; > + } > + > + /** > * Method that handles matchers. > */ > private Map matches(String hint, Object preparedPattern, String pattern, >Parameters params, Map objectModel) throws Exception { > @@ -959,6 +980,11 @@ > </xsl:choose> > </xsl:variable> > > + Object selectorContext = > + getSelectorContext( > + "<xsl:value-of select="$selector-type"/>", objectModel, <xsl:value-of >select="$component-param"/> > + ); > + > <!-- loop through all the when cases --> > <xsl:for-each select="map:when"> > <!-- remove all invalid chars from the test expression. The result is used to >form the name of the generated method > @@ -974,7 +1000,7 @@ > <xsl:call-template name="line-number"/> > <xsl:if test="position() > 1"> > else </xsl:if> > - if (isSelected("<xsl:value-of select="$selector-type"/>", ><xsl:apply-templates select="@test"/>, <xsl:value-of select="$component-param"/>, >objectModel)) { > + if (isSelected("<xsl:value-of select="$selector-type"/>", ><xsl:apply-templates select="@test"/>, <xsl:value-of select="$component-param"/>, >objectModel, selectorContext)) { > if (debug_enabled) getLogger().debug("Select <xsl:value-of >select="$selector-type"/> Test <xsl:value-of >select="XSLTFactoryLoader:escape($factory-loader, @test)"/>"); > <xsl:apply-templates/> > } > Index: src/java/org/apache/cocoon/selection/RequestParameterSelector.java > =================================================================== > RCS file: >/home/cvspublic/xml-cocoon2/src/java/org/apache/cocoon/selection/RequestParameterSelector.java,v > retrieving revision 1.4 > diff -u -r1.4 RequestParameterSelector.java > --- src/java/org/apache/cocoon/selection/RequestParameterSelector.java 22 Feb >2002 07:03:54 -0000 1.4 > +++ src/java/org/apache/cocoon/selection/RequestParameterSelector.java 15 Mar >2002 11:36:15 -0000 > @@ -53,7 +53,6 @@ > import org.apache.avalon.framework.configuration.Configurable; > import org.apache.avalon.framework.configuration.Configuration; > import org.apache.avalon.framework.configuration.ConfigurationException; > -import org.apache.avalon.framework.logger.AbstractLoggable; > import org.apache.avalon.framework.parameters.Parameters; > import org.apache.avalon.framework.thread.ThreadSafe; > > @@ -74,10 +73,11 @@ > * @author <a href="mailto:[EMAIL PROTECTED]">Christian Haul</a> > * @author <a href="mailto:[EMAIL PROTECTED]">Sylvain Wallez</a> > * @author <a href="mailto:[EMAIL PROTECTED]">Vadim Gritsenko</a> > + * @author <a href="mailto:[EMAIL PROTECTED]">Marcus Crafter</a> > * @version CVS $Id: RequestParameterSelector.java,v 1.4 2002/02/22 07:03:54 >cziegeler Exp $ > */ > -public class RequestParameterSelector extends AbstractLoggable > - implements Configurable, ThreadSafe, Selector { > +public class RequestParameterSelector extends AbstractContextualizableSelector > + implements Configurable { > > protected String defaultName; > > @@ -85,20 +85,24 @@ > this.defaultName = config.getChild("parameter-name").getValue(null); > } > > - public boolean select(String expression, Map objectModel, Parameters >parameters) { > + public Object getSelectorContext(Map objectModel, Parameters parameters) { > + > String name = parameters.getParameter("parameter-name", this.defaultName); > > if (name == null) { > getLogger().warn("No parameter name given -- failing."); > - return false; > + return null; > } > > - String value = ObjectModelHelper.getRequest(objectModel).getParameter(name); > - if (value == null) { > - getLogger().debug("Request parameter '" + name + "' not set -- >failing."); > + return ObjectModelHelper.getRequest(objectModel).getParameter(name); > + } > + > + public boolean select(String expression, Object selectorContext) { > + if (selectorContext == null) { > + getLogger().debug("Request parameter '" + selectorContext + "' not set >-- failing."); > return false; > } > > - return value.equals(expression); > + return selectorContext.equals(expression); > } > } > > --------------------------------------------------------------------- > To unsubscribe, e-mail: [EMAIL PROTECTED] > For additional commands, email: [EMAIL PROTECTED] -- ..... ,,$$$$$$$$$, Marcus Crafter ;$' '$$$$: Computer Systems Engineer $: $$$$: ManageSoft GmbH $ o_)$$$: 82-84 Mainzer Landstrasse ;$, _/\ &&:' 60327 Frankfurt Germany ' /( &&& \_&&&&' &&&&. &&&&&&&: --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, email: [EMAIL PROTECTED]