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
       '     /( &&&
           \_&&&&'
          &&&&.
    &&&&&&&:
/*

 ============================================================================
                   The Apache Software License, Version 1.1
 ============================================================================

 Copyright (C) 1999-2002 The Apache Software Foundation. All rights reserved.

 Redistribution and use in source and binary forms, with or without modifica-
 tion, are permitted provided that the following conditions are met:

 1. Redistributions of  source code must  retain the above copyright  notice,
    this list of conditions and the following disclaimer.

 2. Redistributions in binary form must reproduce the above copyright notice,
    this list of conditions and the following disclaimer in the documentation
    and/or other materials provided with the distribution.

 3. The end-user documentation included with the redistribution, if any, must
    include  the following  acknowledgment:  "This product includes  software
    developed  by the  Apache Software Foundation  (http://www.apache.org/)."
    Alternately, this  acknowledgment may  appear in the software itself,  if
    and wherever such third-party acknowledgments normally appear.

 4. The names "Apache Cocoon" and  "Apache Software Foundation" must  not  be
    used to  endorse or promote  products derived from  this software without
    prior written permission. For written permission, please contact
    [EMAIL PROTECTED]

 5. Products  derived from this software may not  be called "Apache", nor may
    "Apache" appear  in their name,  without prior written permission  of the
    Apache Software Foundation.

 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 FITNESS  FOR A PARTICULAR  PURPOSE ARE  DISCLAIMED.  IN NO  EVENT SHALL  THE
 APACHE SOFTWARE  FOUNDATION  OR ITS CONTRIBUTORS  BE LIABLE FOR  ANY DIRECT,
 INDIRECT, INCIDENTAL, SPECIAL,  EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLU-
 DING, BUT NOT LIMITED TO, PROCUREMENT  OF SUBSTITUTE GOODS OR SERVICES; LOSS
 OF USE, DATA, OR  PROFITS; OR BUSINESS  INTERRUPTION)  HOWEVER CAUSED AND ON
 ANY  THEORY OF LIABILITY,  WHETHER  IN CONTRACT,  STRICT LIABILITY,  OR TORT
 (INCLUDING  NEGLIGENCE OR  OTHERWISE) ARISING IN  ANY WAY OUT OF THE  USE OF
 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

 This software  consists of voluntary contributions made  by many individuals
 on  behalf of the Apache Software  Foundation and was  originally created by
 Stefano Mazzocchi  <[EMAIL PROTECTED]>. For more  information on the Apache
 Software Foundation, please see <http://www.apache.org/>.

*/
package org.apache.cocoon.selection;

import org.apache.avalon.framework.component.Component;
import org.apache.avalon.framework.parameters.Parameters;
import org.apache.avalon.framework.logger.AbstractLoggable;
import org.apache.cocoon.selection.Selector;
import java.util.Map;

/**
 * Abstract ContextualizableSelector class.
 *
 * @author <a href="mailto:[EMAIL PROTECTED]";>Marcus Crafter</a>
 * @author <a href="mailto:[EMAIL PROTECTED]";>Sylvain Wallez</a>
 * @version CVS $Id: Selector.java,v 1.4 2002/02/22 07:03:54 cziegeler Exp $
 */
public abstract class AbstractContextualizableSelector extends AbstractLoggable
    implements ContextualizableSelector {

    /**
     * Method to create a selector context.
     *
     * @param objectModel The <code>Map</code> containing object of the
     *                    calling environment which may be used
     *                    to select values to test the expression.
     * @param parameters  The sitemap parameters, as specified by
     *                    &lt;parameter/&gt; tags.
     * @return selector context
     */
    public abstract Object getSelectorContext(Map objectModel, Parameters parameters);

    /**
     * Selectors test pattern against some objects in a <code>Map</code>
     * model and signals success with the returned boolean value
     * @param expression  The expression to test.
     * @return boolean    Signals successful test.
     */
    public abstract boolean select(String expression, Object selectorContext);

    /**
     * Selectors test pattern against some objects in a <code>Map</code>
     * model and signals success with the returned boolean value
     * @param expr        The expression to test.
     * @return Signals successful test.
     */
    public boolean select(String expr, Map objectModel, Parameters params) {
        return select(expr, getSelectorContext(objectModel, params));
    }
}


/*

 ============================================================================
                   The Apache Software License, Version 1.1
 ============================================================================

 Copyright (C) 1999-2002 The Apache Software Foundation. All rights reserved.

 Redistribution and use in source and binary forms, with or without modifica-
 tion, are permitted provided that the following conditions are met:

 1. Redistributions of  source code must  retain the above copyright  notice,
    this list of conditions and the following disclaimer.

 2. Redistributions in binary form must reproduce the above copyright notice,
    this list of conditions and the following disclaimer in the documentation
    and/or other materials provided with the distribution.

 3. The end-user documentation included with the redistribution, if any, must
    include  the following  acknowledgment:  "This product includes  software
    developed  by the  Apache Software Foundation  (http://www.apache.org/)."
    Alternately, this  acknowledgment may  appear in the software itself,  if
    and wherever such third-party acknowledgments normally appear.

 4. The names "Apache Cocoon" and  "Apache Software Foundation" must  not  be
    used to  endorse or promote  products derived from  this software without
    prior written permission. For written permission, please contact
    [EMAIL PROTECTED]

 5. Products  derived from this software may not  be called "Apache", nor may
    "Apache" appear  in their name,  without prior written permission  of the
    Apache Software Foundation.

 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 FITNESS  FOR A PARTICULAR  PURPOSE ARE  DISCLAIMED.  IN NO  EVENT SHALL  THE
 APACHE SOFTWARE  FOUNDATION  OR ITS CONTRIBUTORS  BE LIABLE FOR  ANY DIRECT,
 INDIRECT, INCIDENTAL, SPECIAL,  EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLU-
 DING, BUT NOT LIMITED TO, PROCUREMENT  OF SUBSTITUTE GOODS OR SERVICES; LOSS
 OF USE, DATA, OR  PROFITS; OR BUSINESS  INTERRUPTION)  HOWEVER CAUSED AND ON
 ANY  THEORY OF LIABILITY,  WHETHER  IN CONTRACT,  STRICT LIABILITY,  OR TORT
 (INCLUDING  NEGLIGENCE OR  OTHERWISE) ARISING IN  ANY WAY OUT OF THE  USE OF
 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

 This software  consists of voluntary contributions made  by many individuals
 on  behalf of the Apache Software  Foundation and was  originally created by
 Stefano Mazzocchi  <[EMAIL PROTECTED]>. For more  information on the Apache
 Software Foundation, please see <http://www.apache.org/>.

*/
package org.apache.cocoon.selection;

import org.apache.avalon.framework.component.Component;
import org.apache.avalon.framework.parameters.Parameters;
import org.apache.avalon.framework.thread.ThreadSafe;
import org.apache.cocoon.selection.Selector;
import java.util.Map;

/**
 * ContextualizableSelector is an enhanced Selector interface that allows a
 * context object to be created to optimize selector conditional testing.
 *
 * <p>
 * The original Selector interface supports an <code>if-then-else</code> style
 * of conditional testing depending on whether a particular expression is true.
 * This causes Selector.select() to be invoked for each &lt;map:when&gt;
 * statement which may be undesirable due to performance or logic reasons.
 * </p>
 *
 * <p>
 *  <pre>
 *  Example, the following sitemap snippet:
 *
 *  &lt;map:select type="aSelector"&gt;
 *   &lt;map:when test="test-expr1"&gt;...&lt;/map:when&gt;
 *   &lt;map:when test="test-expr2"&gt;...&lt;/map:when&gt;
 *  &lt;/map:select&gt;
 *
 *  is interpreted as (pseudo-code):
 *
 *  if (aSelector.select("test-expr1", objectModel, params)) {
 *   ...
 *  } else if (aSelector.select("test-expr2", objectModel, params)) {
 *   ...
 *  }
 *
 *  ie. aSelector.select(...) is called once for each &lt;map:when&gt;
 *  statement.
 *  </pre>
 * </p>
 *
 * <p>
 * ContextualizableSelector allows the developer to first create a
 * context object which is passed with each call to select(). This context
 * object is created before any conditional tests are made, and hence can be
 * used to optimize conditional testing.
 * </p>
 *
 * <p>
 *  <pre>
 *  The above example implemented as a ContextualizableSelector would be
 *  interpreted as (psuedo-code):
 *
 *  Object selectorContext = aSelector.getSelectorContext(objectModel, params);
 *  
 *  if (aSelector.select("test-expr1", selectorContext)) {
 *   ...
 *  else if (aSelector.select("test-expr2", selectorContext)) {
 *   ...
 *  }
 *
 *  ie. the bulk of the selector's work is done in getSelectorContext(),
 *  select() simply compares whether the expression should be considered true. 
 *  </pre>
 * </p>
 *
 * @author <a href="mailto:[EMAIL PROTECTED]";>Marcus Crafter</a>
 * @author <a href="mailto:[EMAIL PROTECTED]";>Sylvain Wallez</a>
 * @version CVS $Id: Selector.java,v 1.4 2002/02/22 07:03:54 cziegeler Exp $
 */
public interface ContextualizableSelector extends Selector, ThreadSafe {

    String ROLE = "org.apache.cocoon.selection.ContextualizableSelector";

    /**
     * Method to create a selector context.
     *
     * @param objectModel The <code>Map</code> containing object of the
     *                    calling environment which may be used
     *                    to select values to test the expression.
     * @param parameters  The sitemap parameters, as specified by
     *                    &lt;parameter/&gt; tags.
     * @return            Selector context
     */
    Object getSelectorContext(Map objectModel, Parameters parameters);

    /**
     * Contextualizable Selectors test patterns against a context object
     * and signal success with the returned boolean value
     * @param expression  The expression to test.
     * @param selectorContext The context this test should be performed in.
     * @return            true if the test was successful.
     */
    boolean select(String expression, Object selectorContext);
}


? 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]

Reply via email to