You are viewing a plain text version of this content. The canonical link for it is here.
Posted to adffaces-commits@incubator.apache.org by aw...@apache.org on 2006/10/20 16:48:01 UTC

svn commit: r466196 - in /incubator/adffaces/branches/faces-1_2-second/trinidad: trinidad-api/src/site/xdoc/tagdoc/tr_forEach.xml trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/taglib/ForEachTag.java

Author: awiner
Date: Fri Oct 20 09:47:59 2006
New Revision: 466196

URL: http://svn.apache.org/viewvc?view=rev&rev=466196
Log:
Merge final two files back into branch.  Seems svn - at least my version - was deeply unhappy with merging from a branch when that branch had removed and re-added a file;  these are those files

Modified:
    incubator/adffaces/branches/faces-1_2-second/trinidad/trinidad-api/src/site/xdoc/tagdoc/tr_forEach.xml
    incubator/adffaces/branches/faces-1_2-second/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/taglib/ForEachTag.java

Modified: incubator/adffaces/branches/faces-1_2-second/trinidad/trinidad-api/src/site/xdoc/tagdoc/tr_forEach.xml
URL: http://svn.apache.org/viewvc/incubator/adffaces/branches/faces-1_2-second/trinidad/trinidad-api/src/site/xdoc/tagdoc/tr_forEach.xml?view=diff&rev=466196&r1=466195&r2=466196
==============================================================================
--- incubator/adffaces/branches/faces-1_2-second/trinidad/trinidad-api/src/site/xdoc/tagdoc/tr_forEach.xml (original)
+++ incubator/adffaces/branches/faces-1_2-second/trinidad/trinidad-api/src/site/xdoc/tagdoc/tr_forEach.xml Fri Oct 20 09:47:59 2006
@@ -25,34 +25,17 @@
    <br/>
 <p>
         The forEach tag is a replacement for the JSTL
-&lt;c:forEach&gt; tag that works with ADF Faces components.  In JSF
-1.1, &lt;c:forEach&gt; cannot be used with any JSF components or tags.
+&lt;c:forEach&gt; tag.  Though as of JSF 1.2/JSP 2.1/JSTL 1.2,
+&lt;c:forEach&gt; can be used with any JSF components or tags,
+it does not support "varStatus".  This tag adds support for varStatus
+(other than "current" which is not supported).
 (<b>Note</b>: this tag is not supported in Facelets, because c:forEach
-is functional in Facelets.  It will also be removed in JSF 1.2/JSP
-2.1, because c:forEach will become functional there too.)  This tag
-brings that functionality to JSF, but it is limited to ADF Faces tags.
-This tag also has several limitations not found in &lt;c:forEach&gt;:
-
-<ul>
-
-<li>&lt;tr:forEach&gt; does not currently support scenarios where the
-size of the "items" list or array changes from one request to the
-next. It may be possible to work around this in specific scenarios by
-manually deleting all children of the parent component
-(&lt;tr:selectOneListbox&gt; in the above example), but this has not
-yet been tested.</li>
-
-<li>&lt;tr:forEach&gt; does not support arbitrary
+is fully functional in Facelets.)  Unlike the old Trinidad tr:forEach
+built with JSF 1.1, however, this tag can be used with any JSP 2.1-based
+tag, JSF or non-JSF.  This tag also has a limitation not found in &lt;c:forEach&gt;:  &lt;tr:forEach&gt; does not currently support arbitrary
 java.util.Collections; it can only iterate over java.util.Lists or
-arrays.</li>
-
-<li>&lt;tr:forEach&gt; executes at the time the JSP tag executes. So
-it does not have access to any EL variables that are created by JSF
-components.  For example, the &lt;tr:table&gt; creates an EL variable
-using the value of the "var" attribute. However, this EL variable is
-not available to &lt;tr:forEach&gt;</li>
-
-</ul></p><h4>Example:</h4><source>
+arrays.</p>
+<h4>Example:</h4><source>
             &lt;tr:selectOneListbox value="#{someValue}"&gt;
               &lt;tr:forEach var="item" items="#{model.listOfItems}"&gt;
                 &lt;tr:selectItem value="#{item.value}" text="#{item.text}"/&gt;
@@ -105,7 +88,7 @@
 <td>varStatus</td><td>String</td><td>No</td><td>
 
           name of the loop status exposed when iterating. The properties
-		'index','count','begin','end','step','current','first','last'
+		'index','count','begin','end','step','first','last'
 		are available through this
         </td>
 </tr>

Modified: incubator/adffaces/branches/faces-1_2-second/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/taglib/ForEachTag.java
URL: http://svn.apache.org/viewvc/incubator/adffaces/branches/faces-1_2-second/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/taglib/ForEachTag.java?view=diff&rev=466196&r1=466195&r2=466196
==============================================================================
--- incubator/adffaces/branches/faces-1_2-second/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/taglib/ForEachTag.java (original)
+++ incubator/adffaces/branches/faces-1_2-second/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/taglib/ForEachTag.java Fri Oct 20 09:47:59 2006
@@ -15,19 +15,26 @@
  */
 package org.apache.myfaces.trinidadinternal.taglib;
 
+import java.io.Serializable;
+
 import java.lang.reflect.Array;
 
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import javax.el.ELContext;
+import javax.el.PropertyNotWritableException;
+import javax.el.ValueExpression;
+import javax.el.VariableMapper;
+
 import javax.faces.context.FacesContext;
-import javax.faces.el.ValueBinding;
 import javax.faces.webapp.UIComponentTag;
 
 import javax.servlet.jsp.JspException;
 import javax.servlet.jsp.JspTagException;
 import javax.servlet.jsp.tagext.TagSupport;
+import javax.servlet.jsp.jstl.core.IndexedValueExpression;
 
 import org.apache.myfaces.trinidad.logging.TrinidadLogger;
 import org.apache.myfaces.trinidad.webapp.ELContextTag;
@@ -56,45 +63,38 @@
  *
  * @author The Oracle ADF Faces Team
  */
-public class ForEachTag extends TagSupport implements ELContextTag
+public class ForEachTag extends TagSupport
 {
-  public void setItems(String items)
+  public void setItems(ValueExpression items)
   {
-    if (!items.startsWith("#{") ||
-        !items.endsWith("}"))
+    if (items.isLiteralText())
       throw new IllegalArgumentException(
-        "\"items\" must be a simple JSF EL expression");
+        "\"items\" must be an EL expression");
     _items = items;
   }
 
-  public void setBegin(String begin)
+  public void setBegin(Integer begin)
   {
     _begin = begin;
   }
 
-  public void setEnd(String end)
+  public void setEnd(Integer end)
   {
     _end = end;
   }
 
-  public void setStep(String step)
+  public void setStep(Integer step)
   {
     _step = step;
   }
 
   public void setVar(String var)
   {
-    if (UIComponentTag.isValueReference(var))
-      throw new IllegalArgumentException("\"var\" cannot be an expression");
-
     _var = var;
   }
 
   public void setVarStatus(String varStatus)
   {
-    if (UIComponentTag.isValueReference(varStatus))
-      throw new IllegalArgumentException("\"varStatus\" cannot be an expression");
-
     _varStatus = varStatus;
   }
 
@@ -104,13 +104,13 @@
     _validateAttributes();
 
     FacesContext context = FacesContext.getCurrentInstance();
-    _parentELContext = (ELContextTag)
-       findAncestorWithClass(this, ELContextTag.class);
-    _currentBegin = _resolveInteger(context, _begin, 0);
+    _currentBegin = (_begin == null) ? 0 : _begin.intValue();
     int length;
+
     if (null != _items)
     {
-      Object items = _resolveObject(context, _items);
+      Object items = _items.getValue(pageContext.getELContext());
+
       //pu: If items is specified and resolves to null, it is treated as an
       //  empty collection, i.e., no iteration is performed.
       if (items == null)
@@ -119,6 +119,8 @@
           _LOG.fine("Items expression " + _items + " resolved to null.");
         return SKIP_BODY;
       }
+
+      _itemsValue = items;
       // =-=AEW <c:forEach> supports arbitrary collections;  but
       // JSF only supports List in its EL.
       if (items instanceof List)
@@ -141,7 +143,8 @@
           _LOG.fine("Size of 'items' is less than 'begin'");
         return SKIP_BODY;
       }
-      _currentEnd = _resolveInteger(context, _end, length - 1);
+
+      _currentEnd = (_end == null) ? length - 1 : _end.intValue();
       //pu: If 'end' were specified, but is beyond the size of collection, limit
       //  the iteration to where the collection ends. A mimic of c:forEach and
       //  fix for bug 4029853.
@@ -150,41 +153,35 @@
     }
     else
     {
-      _currentEnd = _resolveInteger(context, _end, 0);
+      _currentEnd = (_end == null) ? 0 : _end.intValue();
     }
     _currentIndex = _currentBegin;
-    _currentStep = _resolveInteger(context, _step, 1);
+    _currentStep = (_step == null) ? 1 : _step.intValue();
     //pu: Now check the valid relation between 'begin','end' and validity of 'step'
     _validateRangeAndStep();
+    // If we can bail, do it now
     if (_currentEnd < _currentIndex)
       return SKIP_BODY;
 
-    if (null != _var || null != _varStatus)
-    {
-      //pu: If items not defined (syntax 2), the return type of 'var' is an
-      //  int according to JSTL specs, and apache impl returns index. Mimic.
-      _varReplacement = (null == _items)?
-        String.valueOf(_currentIndex):
-        _items.substring(2, _items.length() - 1) + "[" + _currentIndex + "]";
-    }
-    //pu: If there is no varStatus set, no point in keeping loop status
-    //  variables updated.
+    // Save off the previous deferred variables
+    VariableMapper vm = 
+      pageContext.getELContext().getVariableMapper();
+    if (_var != null)
+      _previousDeferredVar = vm.resolveVariable(_var);
+
     if (null != _varStatus)
     {
-      _updateLoopStatus();
+      _previousDeferredVarStatus = vm.resolveVariable(_varStatus);
       _propertyReplacementMap = new HashMap<String, Object>(9, 1);
-      _propertyReplacementMap.put("begin", new Integer(_currentBegin));
-      _propertyReplacementMap.put("end", new Integer(_currentEnd));
-      _propertyReplacementMap.put("step", new Integer(_currentStep));
-      _propertyReplacementMap.put("count", new Integer(_currentCount));
-      _propertyReplacementMap.put("index", new Integer(_currentIndex));
-      _propertyReplacementMap.put("current", _varReplacement);
-      _propertyReplacementMap.put(
-        "first",
-        (_isFirst)? Boolean.TRUE:Boolean.FALSE);
-      _propertyReplacementMap.put(
-        "last",
-        (_isLast)? Boolean.TRUE:Boolean.FALSE);
+      _propertyReplacementMap.put("begin", _currentBegin);
+      _propertyReplacementMap.put("end", _currentEnd);
+      _propertyReplacementMap.put("step", _currentStep);
+      _propertyReplacementMap.put("count", _currentCount);
+      _propertyReplacementMap.put("index", _currentIndex);
+      // FIXME: Can we support "current" efficiently?
+      //      _propertyReplacementMap.put("current", _varReplacement);
+      _propertyReplacementMap.put("first", _isFirst);
+      _propertyReplacementMap.put("last", _isLast);
     }
 
     if (_LOG.isFiner())
@@ -192,6 +189,10 @@
       _LOG.finer("Iterating from " + _currentIndex + " to " + _currentEnd +
                  " by " + _currentStep);
     }
+
+    // Update the variables
+    _updateVars();
+
     return EVAL_BODY_INCLUDE;
   }
 
@@ -200,102 +201,46 @@
   {
     _currentIndex += _currentStep;
 
-    if (null != _var || null != _varStatus)
-    {
-      //pu: If items not defined (syntax 2), the return type of 'var' is an
-      //  int according to JSTL specs, and apache impl returns index. Mimic.
-      _varReplacement = (null == _items)?
-        String.valueOf(_currentIndex):
-        _items.substring(2, _items.length() - 1) + "[" + _currentIndex + "]";
-    }
-
     //pu: if there is no varStatus set, no point in keeping loop status
     //  variables updated.
     if (null != _varStatus)
     {
       //pu: _isFirst is not yet updated after first iteration
       boolean isSecondIteration = (_isFirst)? true:false;
-      _updateLoopStatus();
       if (isSecondIteration)
       {
-        _propertyReplacementMap.put(
-          "first",
-          (_isFirst)? Boolean.TRUE:Boolean.FALSE);
+        _propertyReplacementMap.put("first", _isFirst);
       }
       if (_isLast)
       {
-        _propertyReplacementMap.put(
-          "last",
-          (_isLast)? Boolean.TRUE:Boolean.FALSE);
+        _propertyReplacementMap.put("last", _isLast);
       }
-      _propertyReplacementMap.put("count", new Integer(_currentCount));
-      _propertyReplacementMap.put("index", new Integer(_currentIndex));
-      _propertyReplacementMap.put("current", _varReplacement);
+      _propertyReplacementMap.put("count", _currentCount);
+      _propertyReplacementMap.put("index", _currentIndex);
+      // FIXME Can we support "current" efficiently?
+      //      _propertyReplacementMap.put("current", _varReplacement);
     }
 
+    // If we're at the end, bail
     if (_currentEnd < _currentIndex)
-      return SKIP_BODY;
-    return EVAL_BODY_AGAIN;
-  }
-
-  public String transformId(String id)
-  {
-    if (_parentELContext != null)
-      id = _parentELContext.transformId(id);
-
-    // SEPARATOR_CHAR would be nice;  but JSF does not allow
-    // the separator char in an ID - just in client IDs.
-    //    return id + NamingContainer.SEPARATOR_CHAR + _currentIndex;
-    return id + '_' + _currentIndex;
-  }
-  static String __transformExpression(
-    String expression,
-    String var,
-    String subst)
-  {
-    String varDot = var + ".";
-    Tokenizer tokens = new Tokenizer(expression);
-    StringBuffer buf = new StringBuffer(expression.length());
-    while(tokens.hasNext())
-    {
-      Token tok = tokens.next();
-      String exp = tok.getText();
-      if (tok.type == Tokenizer.VAR_TYPE)
-      {
-        if (var.equals(exp) || exp.startsWith(varDot))
-        {
-          buf.append(subst);
-          buf.append(exp.substring(var.length()));
-          continue;
-        }
-      }
-
-      buf.append(exp);
-    }
-    return buf.toString();
-  }
-  public String transformExpression(String expression)
-  {
-    if (expression != null)
     {
-      String transformedExp = expression;
-      int expressionStart = expression.indexOf("#{");
-      if (expressionStart >= 0)
-      {
-        transformedExp = _transformExpression(expression);
+      // Restore EL state
+      VariableMapper vm = 
+        pageContext.getELContext().getVariableMapper();
+      if (_var != null)
+        vm.setVariable(_var, _previousDeferredVar);
+      if (_varStatus != null)
+        vm.setVariable(_varStatus, _previousDeferredVarStatus);
 
-        if (_parentELContext != null)
-          transformedExp = _parentELContext.transformExpression(transformedExp);
-      }
-
-      if (_LOG.isFiner())
-        _LOG.finer("Transformed expression:{0} to:{1}",
-                   new String[] {expression, transformedExp});
-      return transformedExp;
+      return SKIP_BODY;
     }
+    
+    // Otherwise, update the variables and go again
+    _updateVars();
 
-    return null;
+    return EVAL_BODY_AGAIN;
   }
+
   /**
    * Release state.
    */
@@ -303,25 +248,46 @@
   public void release()
   {
     super.release();
-    //=-=pu: Does only the properties that has setters need to be released ?
-    //  What about variables like _propertyReplacementMap/_varReplacement etc. ?
     _begin = null;
     _end = null;
-    _end = null;
-    _items = null;
     _step = null;
+    _items = null;
+    _itemsValue = null;
     _var = null;
     _varStatus = null;
-  }
-
-  protected ValueBinding createValueBinding(
-    FacesContext context,
-    String       expression)
-  {
-    if (_parentELContext != null)
-      expression = _parentELContext.transformExpression(expression);
+    _propertyReplacementMap = null;
+    _previousDeferredVar = null;
+    _previousDeferredVarStatus = null;
+  }
+
+  // Push new values into the VariableMapper and the pageContext
+  private void _updateVars()
+  {
+    VariableMapper vm = 
+      pageContext.getELContext().getVariableMapper();
+    if (_var != null)
+    {
+      ValueExpression iterated = new IndexedValueExpression(_items,
+                                                            _currentIndex);
+      vm.setVariable(_var, iterated);
+      
+      Object items = _itemsValue;
+      Object item;
+      if (items instanceof List)
+        item = ((List) items).get(_currentIndex);
+      else
+        item = Array.get(items, _currentIndex);
 
-    return context.getApplication().createValueBinding(expression);
+      pageContext.setAttribute(_var, item);
+    }
+    
+    if (_varStatus != null)
+    {
+      pageContext.setAttribute(_varStatus, _propertyReplacementMap);
+      ValueExpression constant = new Constants(
+                                      new HashMap(_propertyReplacementMap));
+      vm.setVariable(_varStatus, constant);
+    }
   }
 
   private void _validateAttributes() throws JspTagException
@@ -349,154 +315,62 @@
     if (_currentStep < 1)
       throw new JspTagException("'step' < 1");
   }
-  
-  private String _transformExpression(String expression)
+
+  // Basic ValueExpression that always returns a constant object
+  static private class Constants extends ValueExpression
+                                 implements Serializable
   {
-    boolean doVar = (_var != null);
-    boolean doVarStatus = (_varStatus != null);
-    if (!(doVar || doVarStatus))
-      return expression;
-    StringBuffer buf = new StringBuffer(expression.length());
-    // ACW: see bug 3754666:
-    Tokenizer tokens = new Tokenizer(expression);
-    String varDot = _var+".";
-    String varStatusDot = _varStatus+".";
-    while(tokens.hasNext())
-    {
-      Token tok = tokens.next();
-      String text = tok.getText();
-      if (tok.type == Tokenizer.VAR_TYPE)
-      {
-        if (doVar && (_var.equals(text) || text.startsWith(varDot)))
-        {
-          text = _replaceVariableAndPropertiesInExpression(
-            text, _var, _varReplacement, null);
-        }
-        else if (doVarStatus && (_varStatus.equals(text) || text.startsWith(varStatusDot)))
-        {
-          text = _replaceVariableAndPropertiesInExpression(
-            text, _varStatus, null, _propertyReplacementMap);
-        }
-      }
-      buf.append(text);
+    public Constants(Object o)
+    {
+      _o = o;
     }
-    return buf.toString();
-  }
-  /**
-   * Replaces all occurance of 'variable' and the property (the key in map)
-   * with the property replacement (value in the map).
-   * If propertyReplacementMap were to be null, then all the occurance of
-   * 'variable' will be replaced by 'variableReplacement'.
-   * Returns a string modified thus.
-   */
-  private String _replaceVariableAndPropertiesInExpression(
-    String subExpression,
-    String variable,
-    String variableReplacement,
-    Map<String, Object> propertyReplacementMap)
-  {
-    int variableLength = variable.length();
-    //pu: Now check whether the variable is followed by any property from
-    //  the supplied map.
-    if (null != propertyReplacementMap)
-    {
-      String property;
-      String propertyReplacement;
-      for(Map.Entry<String, Object> entry : propertyReplacementMap.entrySet())
-      {
-        property = entry.getKey();
-        String expressionAfterVar = subExpression.substring(variableLength);
-        if (expressionAfterVar.startsWith("."+property))
-        {
-          int propertyLength = property.length();
-          //pu: We found our property, but it could be followed
-          //  by an alphanumeric in which case we ignore and move on because
-          //  we just found it as a substring
-          int endOfReplacement = propertyLength + 1;
-          if (expressionAfterVar.length() > endOfReplacement)
-          {
-            if (Character.isLetterOrDigit(
-              expressionAfterVar.charAt(endOfReplacement)))
-            {
-              continue;
-            }
-          }
-          propertyReplacement = entry.getValue().toString();
-          //pu: Replace both the variable plus the property following it.
-          subExpression = _replaceSubString(
-            subExpression,
-            0,
-            variableLength + propertyLength + 1,
-            propertyReplacement);
-          //pu: If we handled atleast one property, break out from here.
-          break;
-        }
-      }
+
+    public Object getValue(ELContext context)
+    {
+      return _o;
     }
-    //pu: If there were no properties to be replaced, replace the variable itself
-    else
+
+    public void setValue(ELContext context, Object value)
     {
-      subExpression = _replaceSubString(subExpression, 0, variableLength, variableReplacement);
+      throw new PropertyNotWritableException();
     }
 
+    public boolean isReadOnly(ELContext context)
+    {
+      return true;
+    }
 
-    return subExpression;
-  }
-  /**
-   * Given the 'str', replaces a substring starting from 'beginIndex'
-   * of 'noOfChars' length with the string in 'replacement', returns the
-   * string modified thus.
-   */
-  private String _replaceSubString(
-    String str,
-    int beginIndex,
-    int noOfChars,
-    String replacement)
-  {
-    StringBuffer buffer = new StringBuffer(str.length() +
-                                           replacement.length() -
-                                           noOfChars);
-    buffer.append(str.substring(0, beginIndex));
-    buffer.append(replacement);
-    buffer.append(str.substring(beginIndex + noOfChars));
-    return buffer.toString();
-  }
-  /**
-   * Update the loop status variables.
-   */
-  private void _updateLoopStatus()
-  {
-    _currentCount = ((_currentIndex - _currentBegin)/_currentStep) + 1;
-    _isFirst = (_currentIndex == _currentBegin);
-    _isLast = (_currentIndex + _currentStep) > _currentEnd;
-  }
+    public Class getType(ELContext context)
+    {
+      return _o.getClass();
+    }
 
+    public Class getExpectedType()
+    {
+      return _o.getClass();
+    }
 
-  private Object _resolveObject(FacesContext context, String expression)
-  {
-    ValueBinding vb = createValueBinding(context, expression);
-    return vb.getValue(context);
-  }
+    public String getExpressionString()
+    {
+      return null;
+    }
 
-  private int _resolveInteger(
-    FacesContext context,
-    String       expression,
-    int          defaultValue)
-  {
-    if (expression == null)
-      return defaultValue;
+    public boolean equals(Object obj)
+    {
+      return obj == this;
+    }
 
-    if (UIComponentTag.isValueReference(expression))
+    public int hashCode()
     {
-      Object o = _resolveObject(context, expression);
-      if (o instanceof Number)
-        return ((Number) o).intValue();
-      if (o == null)
-        return defaultValue;
+      return _o.hashCode();
+    }
 
-      expression = o.toString();
+    public boolean isLiteralText()
+    {
+      return true;
     }
-    return Integer.parseInt(expression);
+
+    private Object _o;
   }
 
   private int _currentBegin;
@@ -506,18 +380,23 @@
   private int _currentCount;
   private boolean _isFirst;
   private boolean _isLast;
-  private ELContextTag _parentELContext;
 
-  private String _items;
-  private String _begin;
-  private String _end;
-  private String _step;
+
+  private ValueExpression _items;
+  private Object          _itemsValue;
+
+  private Integer _begin;
+  private Integer _end;
+  private Integer _step;
   private String _var;
   private String _varStatus;
-  //pu: Map for properties referred off from 'varStatus' and their replacements
+  
+  // Saved values on the VariableMapper
+  private ValueExpression _previousDeferredVar;
+  private ValueExpression _previousDeferredVarStatus;
+
+  // Map for properties referred off from 'varStatus' and their replacements
   private Map<String, Object> _propertyReplacementMap;
-  //pu: Represents replacement for 'var' upon every iteration
-  private String _varReplacement;
 
   private static final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(ForEachTag.class);