Author: musachy
Date: Thu Feb  5 16:55:14 2009
New Revision: 741179

URL: http://svn.apache.org/viewvc?rev=741179&view=rev
Log:
WW-2984 Improve iterator tag to support "begin", "end" and "step" attributes

Modified:
    
struts/struts2/trunk/core/src/main/java/org/apache/struts2/components/IteratorComponent.java
    
struts/struts2/trunk/core/src/main/java/org/apache/struts2/views/jsp/IteratorTag.java
    struts/struts2/trunk/core/src/site/resources/tags/iterator.html
    
struts/struts2/trunk/core/src/test/java/org/apache/struts2/views/jsp/IteratorTagTest.java

Modified: 
struts/struts2/trunk/core/src/main/java/org/apache/struts2/components/IteratorComponent.java
URL: 
http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/java/org/apache/struts2/components/IteratorComponent.java?rev=741179&r1=741178&r2=741179&view=diff
==============================================================================
--- 
struts/struts2/trunk/core/src/main/java/org/apache/struts2/components/IteratorComponent.java
 (original)
+++ 
struts/struts2/trunk/core/src/main/java/org/apache/struts2/components/IteratorComponent.java
 Thu Feb  5 16:55:14 2009
@@ -23,6 +23,8 @@
 
 import java.io.Writer;
 import java.util.Iterator;
+import java.util.List;
+import java.util.Arrays;
 
 import org.apache.struts2.views.annotations.StrutsTag;
 import org.apache.struts2.views.annotations.StrutsTagAttribute;
@@ -30,6 +32,8 @@
 import org.apache.struts2.views.jsp.IteratorStatus;
 
 import com.opensymphony.xwork2.util.ValueStack;
+import com.opensymphony.xwork2.util.logging.Logger;
+import com.opensymphony.xwork2.util.logging.LoggerFactory;
 
 /**
  * <!-- START SNIPPET: javadoc -->
@@ -49,6 +53,13 @@
  * <li>id (String) - if specified the current iteration object will be place 
with this id in Struts stack's context
  * scope</li>
  *
+ * <li>begin (Integer) - if specified the iteration will start on that 
index</li>
+ *
+ * <li>end (Integer) - if specified the iteration will end on that 
index(inclusive)</li>
+ *
+ * <li>step (Integer) - if specified the iteration index will be increased by 
this value on each iteration. It can be a negative
+ * value, in which case 'begin' must be greater than 'end'</li>
+ *
  * </ul>
  *
  * <!-- END SNIPPET: params -->
@@ -162,19 +173,14 @@
  *
  * <!-- START SNIPPET: example5description -->
  *
- * </p>To simulate a simple loop with iterator tag, the following could be 
done.
- * It does the loop 5 times.
+ * </p>A loop that iterates 5 times
  *
  * <!-- END SNIPPET: example5description -->
  *
  * <pre>
  * <!-- START SNIPPET: example5code -->
  *
- * &lt;s:iterator status="stat" value="{1,2,3,4,5}" &gt;
- *    &lt;!-- grab the index (start with 0 ... ) --&gt;
- *    &lt;s:property value="#stat.index" /&gt;
- *
- *    &lt;!-- grab the top of the stack which should be the --&gt;
+ * &lt;s:iterator var="counter" begin="1" end="5" &gt;
  *    &lt;!-- current iteration value (1, ... 5) --&gt;
  *    &lt;s:property value="top" /&gt;
  * &lt;/s:iterator&gt;
@@ -201,15 +207,39 @@
  * <!-- END SNIPPET: example6code -->
  * </pre>
  *
+ *  <!-- START SNIPPET: example7description -->
+ *
+ * </p>A loop that iterates over a partial list
+ *
+ * <!-- END SNIPPET: example7description -->
+ *
+ * <pre>
+ * <!-- START SNIPPET: example7code -->
+ *
+ * &lt;s:iterator value="{1,2,3,4,5}" begin="2" end="4" &gt;
+ *    &lt;!-- current iteration value (2,3,4) --&gt;
+ *    &lt;s:property value="top" /&gt;
+ * &lt;/s:iterator&gt;
+ *
+ * <!-- END SNIPPET: example7code -->
+ * </pre>
  */
 @StrutsTag(name="iterator", 
tldTagClass="org.apache.struts2.views.jsp.IteratorTag", description="Iterate 
over a iterable value")
 public class IteratorComponent extends ContextBean {
+    private static final Logger LOG = 
LoggerFactory.getLogger(IteratorComponent.class);
+
     protected Iterator iterator;
     protected IteratorStatus status;
     protected Object oldStatus;
     protected IteratorStatus.StatusState statusState;
     protected String statusAttr;
     protected String value;
+    protected String beginStr;
+    protected Integer begin;
+    protected String endStr;
+    protected Integer end;
+    protected String stepStr;
+    protected Integer step;
 
     public IteratorComponent(ValueStack stack) {
         super(stack);
@@ -222,12 +252,50 @@
             status = new IteratorStatus(statusState);
         }
 
+        if (beginStr != null)
+            begin = (Integer) findValue(beginStr,  Integer.class);
+
+        if (endStr != null)
+            end = (Integer) findValue(endStr,  Integer.class);
+
+        if (stepStr != null)
+            step = (Integer) findValue(stepStr,  Integer.class);
+
         ValueStack stack = getStack();
 
-        if (value == null) {
+        if (value == null && begin == null && end == null) {
             value = "top";
         }
-        iterator = MakeIterator.convert(findValue(value));
+        Object iteratorTarget = findValue(value);
+        iterator = MakeIterator.convert(iteratorTarget);
+
+        if (begin != null) {
+            //default step to 1
+            if (step == null)
+                step = 1;
+
+            if (iterator == null) {
+                //classic for loop from 'begin' to 'end'
+                iterator = new CounterIterator(begin, end, step, null);
+            } else if (iterator != null) {
+                //only arrays and lists are supported
+                if (iteratorTarget.getClass().isArray()) {
+                    Object[] values = (Object[]) iteratorTarget;
+                    if (end == null)
+                        end = step > 0 ? values.length - 1 : 0;
+                    iterator = new CounterIterator(begin, end, step, 
Arrays.asList(values));
+                } else if (iteratorTarget instanceof List) {
+                    List values = (List) iteratorTarget;
+                    if (end == null)
+                        end = step > 0 ? values.size() - 1 : 0;
+                    iterator = new CounterIterator(begin, end, step, values);
+                } else {
+                    //so the iterator is not based on an array or a list
+                    LOG.error("Incorrect use of the iterator tag. When 'begin' 
is set, 'value' must be" +
+                            " an Array or a List, or not set at all. 'begin', 
'end' and 'step' will be ignored");
+                }
+            }
+        }
 
         // get the first
         if ((iterator != null) && iterator.hasNext()) {
@@ -289,6 +357,44 @@
         }
     }
 
+    class CounterIterator implements Iterator<Object> {
+        private int step;
+        private int end;
+        private int currentIndex;
+        private List<Object> values;
+
+        CounterIterator(Integer begin, Integer end, Integer step, List<Object> 
values) {
+            this.end = end;
+            if (step != null)
+                this.step = step;
+            this.currentIndex = begin - this.step;
+            this.values = values;
+        }
+
+        public boolean hasNext() {
+            int next = peekNextIndex();
+            return step > 0 ? next <= end : next >= end;
+        }
+
+        public Object next() {
+            if (hasNext()) {
+                int nextIndex = peekNextIndex();
+                currentIndex += step;
+                return value != null ? values.get(nextIndex) : nextIndex;
+            } else {
+                throw new IndexOutOfBoundsException("Index " + ( currentIndex 
+ step) + " must be less than or equal to " + end);
+            }
+        }
+
+        private int peekNextIndex() {
+           return currentIndex + step;
+        }
+
+        public void remove() {
+            throw new UnsupportedOperationException("Values cannot be removed 
from this iterator");
+        }
+    }
+
     @StrutsTagAttribute(description="If specified, an instanceof 
IteratorStatus will be pushed into stack upon each iteration",
         type="Boolean", defaultValue="false")
     public void setStatus(String status) {
@@ -300,4 +406,19 @@
         this.value = value;
     }
 
+    @StrutsTagAttribute(description="if specified the iteration will start on 
that index", type="Integer", defaultValue="0")
+    public void setBegin(String begin) {
+        this.beginStr = begin;
+    }
+
+    @StrutsTagAttribute(description="if specified the iteration will end on 
that index(inclusive)", type="Integer", defaultValue="Size of the 'values' 
collection or array")
+    public void setEnd(String end) {
+        this.endStr = end;
+    }
+
+    @StrutsTagAttribute(description="if specified the iteration index will be 
increased by this value on each iteration. It can be a negative " +
+            "value, in which case 'begin' must be greater than 'end'", 
type="Integer", defaultValue="1")
+    public void setStep(String step) {
+        this.stepStr = step;
+    }
 }

Modified: 
struts/struts2/trunk/core/src/main/java/org/apache/struts2/views/jsp/IteratorTag.java
URL: 
http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/java/org/apache/struts2/views/jsp/IteratorTag.java?rev=741179&r1=741178&r2=741179&view=diff
==============================================================================
--- 
struts/struts2/trunk/core/src/main/java/org/apache/struts2/views/jsp/IteratorTag.java
 (original)
+++ 
struts/struts2/trunk/core/src/main/java/org/apache/struts2/views/jsp/IteratorTag.java
 Thu Feb  5 16:55:14 2009
@@ -39,6 +39,9 @@
 
     protected String statusAttr;
     protected String value;
+    protected String begin;
+    protected String end;
+    protected String step;
 
     public Component getBean(ValueStack stack, HttpServletRequest req, 
HttpServletResponse res) {
         return new IteratorComponent(stack);
@@ -50,6 +53,9 @@
         IteratorComponent tag = (IteratorComponent) getComponent();
         tag.setStatus(statusAttr);
         tag.setValue(value);
+        tag.setBegin(begin);
+        tag.setEnd(end);
+        tag.setStep(step);
     }
 
     public void setStatus(String status) {
@@ -60,6 +66,18 @@
         this.value = value;
     }
 
+    public void setBegin(String begin) {
+        this.begin = begin;
+    }
+
+    public void setEnd(String end) {
+        this.end = end;
+    }
+
+    public void setStep(String step) {
+        this.step = step;
+    }
+
     public int doEndTag() throws JspException {
         component = null;
         return EVAL_PAGE;

Modified: struts/struts2/trunk/core/src/site/resources/tags/iterator.html
URL: 
http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/site/resources/tags/iterator.html?rev=741179&r1=741178&r2=741179&view=diff
==============================================================================
--- struts/struts2/trunk/core/src/site/resources/tags/iterator.html (original)
+++ struts/struts2/trunk/core/src/site/resources/tags/iterator.html Thu Feb  5 
16:55:14 2009
@@ -34,6 +34,22 @@
                                <th align="left" 
valign="top"><h4>Description</h4></th>
                        </tr>
                                <tr>
+                                       <td align="left" valign="top">begin</td>
+                                       <td align="left" valign="top">false</td>
+                                       <td align="left" valign="top">0</td>
+                                       <td align="left" valign="top">false</td>
+                                       <td align="left" 
valign="top">Integer</td>
+                                       <td align="left" valign="top">if 
specified the iteration will start on that index</td>
+                               </tr>
+                               <tr>
+                                       <td align="left" valign="top">end</td>
+                                       <td align="left" valign="top">false</td>
+                                       <td align="left" valign="top">Size of 
the 'values' collection or array</td>
+                                       <td align="left" valign="top">false</td>
+                                       <td align="left" 
valign="top">Integer</td>
+                                       <td align="left" valign="top">if 
specified the iteration will end on that index(inclusive)</td>
+                               </tr>
+                               <tr>
                                        <td align="left" valign="top">id</td>
                                        <td align="left" valign="top">false</td>
                                        <td align="left" valign="top"></td>
@@ -50,6 +66,14 @@
                                        <td align="left" valign="top">If 
specified, an instanceof IteratorStatus will be pushed into stack upon each 
iteration</td>
                                </tr>
                                <tr>
+                                       <td align="left" valign="top">step</td>
+                                       <td align="left" valign="top">false</td>
+                                       <td align="left" valign="top">1</td>
+                                       <td align="left" valign="top">false</td>
+                                       <td align="left" 
valign="top">Integer</td>
+                                       <td align="left" valign="top">if 
specified the iteration index will be increased by this value on each 
iteration. It can be a negative value, in which case 'begin' must be greater 
than 'end'</td>
+                               </tr>
+                               <tr>
                                        <td align="left" valign="top">value</td>
                                        <td align="left" valign="top">false</td>
                                        <td align="left" valign="top"></td>

Modified: 
struts/struts2/trunk/core/src/test/java/org/apache/struts2/views/jsp/IteratorTagTest.java
URL: 
http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/test/java/org/apache/struts2/views/jsp/IteratorTagTest.java?rev=741179&r1=741178&r2=741179&view=diff
==============================================================================
--- 
struts/struts2/trunk/core/src/test/java/org/apache/struts2/views/jsp/IteratorTagTest.java
 (original)
+++ 
struts/struts2/trunk/core/src/test/java/org/apache/struts2/views/jsp/IteratorTagTest.java
 Thu Feb  5 16:55:14 2009
@@ -21,18 +21,19 @@
 
 package org.apache.struts2.views.jsp;
 
+import com.mockobjects.servlet.MockBodyContent;
+import com.mockobjects.servlet.MockJspWriter;
+import org.apache.commons.collections.ListUtils;
+
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.tagext.TagSupport;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
-import javax.servlet.jsp.JspException;
-import javax.servlet.jsp.tagext.TagSupport;
-
-import com.mockobjects.servlet.MockBodyContent;
-import com.mockobjects.servlet.MockJspWriter;
-
 
 /**
  * Test Case for Iterator Tag
@@ -352,6 +353,294 @@
         validateSkipBody();
     }
 
+    public void testCounter() throws JspException {
+        tag.setBegin("0");
+        tag.setEnd("5");
+        validateCounter(new Integer[]{0, 1, 2, 3, 4, 5});
+    }
+
+     public void testCounterWithStackValues() throws JspException {
+        stack.getContext().put("begin", 0);
+        stack.getContext().put("end", 5);
+        tag.setBegin("%{#begin}");
+        tag.setEnd("%{#end}");
+        validateCounter(new Integer[]{0, 1, 2, 3, 4, 5});
+    }
+
+    public void testCounterWithList() throws JspException {
+        Foo foo = new Foo();
+        ArrayList list = new ArrayList();
+        list.add("a");
+        list.add("b");
+        list.add("c");
+        list.add("d");
+        foo.setList(list);
+
+        stack.push(foo);
+
+        tag.setValue("list");
+
+        tag.setBegin("0");
+        tag.setEnd("2");
+        validateCounter(new String[]{"a", "b", "c"});
+    }
+
+    public void testCounterWithArray() throws JspException {
+        Foo foo = new Foo();
+        ArrayList list = new ArrayList();
+        foo.setArray(new String[]{"a", "b", "c", "d"});
+
+        stack.push(foo);
+
+        tag.setValue("array");
+
+        tag.setBegin("0");
+        tag.setEnd("2");
+        validateCounter(new String[]{"a", "b", "c"});
+    }
+
+
+    public void testCounterWithListNoEnd() throws JspException {
+        Foo foo = new Foo();
+        ArrayList list = new ArrayList();
+        list.add("a");
+        list.add("b");
+        list.add("c");
+        list.add("d");
+        foo.setList(list);
+
+        stack.push(foo);
+
+        tag.setValue("list");
+
+        tag.setBegin("0");
+        validateCounter(new String[]{"a", "b", "c", "d"});
+    }
+
+    public void testCounterWithArrayNoEnd() throws JspException {
+        Foo foo = new Foo();
+        ArrayList list = new ArrayList();
+        foo.setArray(new String[]{"a", "b", "c", "d"});
+
+        stack.push(foo);
+
+        tag.setValue("array");
+
+        tag.setBegin("0");
+        validateCounter(new String[]{"a", "b", "c", "d"});
+    }
+
+    public void testCounterWithList2() throws JspException {
+        Foo foo = new Foo();
+        ArrayList list = new ArrayList();
+        list.add("a");
+        list.add("b");
+        list.add("c");
+        list.add("d");
+        foo.setList(list);
+
+        stack.push(foo);
+
+        tag.setValue("list");
+
+        tag.setBegin("1");
+        tag.setEnd("2");
+        validateCounter(new String[]{"b", "c"});
+    }
+
+    public void testCounterWithArray2() throws JspException {
+        Foo foo = new Foo();
+        ArrayList list = new ArrayList();
+        foo.setArray(new String[]{"a", "b", "c", "d"});
+
+        stack.push(foo);
+
+        tag.setValue("array");
+
+        tag.setBegin("1");
+        tag.setEnd("2");
+        validateCounter(new String[]{"b", "c"});
+    }
+
+    public void testCounterWithListNoEnd2() throws JspException {
+        Foo foo = new Foo();
+        ArrayList list = new ArrayList();
+        list.add("a");
+        list.add("b");
+        list.add("c");
+        list.add("d");
+        foo.setList(list);
+
+        stack.push(foo);
+
+        tag.setValue("list");
+
+        tag.setBegin("2");
+        validateCounter(new String[]{"c", "d"});
+    }
+
+     public void testCounterWithArrayNoEnd2() throws JspException {
+        Foo foo = new Foo();
+        ArrayList list = new ArrayList();
+        foo.setArray(new String[]{"a", "b", "c", "d"});
+
+        stack.push(foo);
+
+        tag.setValue("array");
+
+        tag.setBegin("2");
+        validateCounter(new String[]{"c", "d"});
+    }
+
+    public void testCounter2() throws JspException {
+        tag.setBegin("2");
+        tag.setEnd("5");
+        validateCounter(new Integer[]{2, 3, 4, 5});
+    }
+
+    public void testCounterWithStep() throws JspException {
+        tag.setBegin("0");
+        tag.setEnd("5");
+        tag.setStep("2");
+        validateCounter(new Integer[]{0, 2, 4});
+    }
+
+     public void testCounterWithListAndStep() throws JspException {
+        Foo foo = new Foo();
+        ArrayList list = new ArrayList();
+        list.add("a");
+        list.add("b");
+        list.add("c");
+        list.add("d");
+        foo.setList(list);
+
+        stack.push(foo);
+
+        tag.setValue("list");
+
+        tag.setStep("2");
+        tag.setBegin("0");
+        tag.setEnd("3");
+
+        validateCounter(new String[]{"a", "c"});
+    }
+
+     public void testCounterWithArrayAndStep() throws JspException {
+        Foo foo = new Foo();
+        ArrayList list = new ArrayList();
+        foo.setArray(new String[]{"a", "b", "c", "d"});
+
+        stack.push(foo);
+
+        tag.setValue("array");
+
+        tag.setStep("2");
+        tag.setBegin("0");
+        tag.setEnd("3");
+
+        validateCounter(new String[]{"a", "c"});
+    }
+
+    public void testCounterWithListAndStepNoEnd() throws JspException {
+        Foo foo = new Foo();
+        ArrayList list = new ArrayList();
+        list.add("a");
+        list.add("b");
+        list.add("c");
+        list.add("d");
+        foo.setList(list);
+
+        stack.push(foo);
+
+        tag.setValue("list");
+
+        tag.setStep("2");
+        tag.setBegin("0");
+
+        validateCounter(new String[]{"a", "c"});
+    }
+
+    public void testCounterWithArrayAndStepNoEnd() throws JspException {
+        Foo foo = new Foo();
+        ArrayList list = new ArrayList();
+        foo.setArray(new String[]{"a", "b", "c", "d"});
+
+        stack.push(foo);
+
+        tag.setValue("array");
+
+        tag.setStep("2");
+        tag.setBegin("0");
+
+        validateCounter(new String[]{"a", "c"});
+    }
+
+    public void testCounterWithNegativeStep() throws JspException {
+        tag.setBegin("8");
+        tag.setEnd("5");
+        tag.setStep("-1");
+        validateCounter(new Integer[]{8, 7, 6, 5});
+    }
+
+    public void testCounterWithListAndNegativeStep() throws JspException {
+        Foo foo = new Foo();
+        ArrayList list = new ArrayList();
+        list.add("a");
+        list.add("b");
+        list.add("c");
+        list.add("d");
+        foo.setList(list);
+
+        stack.push(foo);
+
+        tag.setValue("list");
+
+        tag.setStep("-1");
+        tag.setBegin("3");
+        tag.setEnd("1");
+
+        validateCounter(new String[]{"d", "c", "b"});
+    }
+
+     public void testCounterWithArrayAndNegativeStep() throws JspException {
+        Foo foo = new Foo();
+        ArrayList list = new ArrayList();
+        list.add("a");
+        list.add("b");
+        list.add("c");
+        list.add("d");
+        foo.setList(list);
+
+        stack.push(foo);
+
+        tag.setValue("list");
+
+        tag.setStep("-1");
+        tag.setBegin("3");
+        tag.setEnd("1");
+
+        validateCounter(new String[]{"d", "c", "b"});
+    }
+
+    protected void validateCounter(Object[] expectedValues) throws 
JspException {
+        List values = new ArrayList();
+        try {
+            int result = tag.doStartTag();
+            assertEquals(result, TagSupport.EVAL_BODY_INCLUDE);
+            values.add(stack.getRoot().peek());
+        } catch (JspException e) {
+            e.printStackTrace();
+            fail();
+        }
+
+        while (tag.doAfterBody() == TagSupport.EVAL_BODY_AGAIN) {
+            values.add(stack.getRoot().peek());
+        }
+
+        assertEquals(expectedValues.length, values.size());
+        ListUtils.isEqualList(Arrays.asList(expectedValues), values);
+    }
+
     protected void setUp() throws Exception {
         super.setUp();
 


Reply via email to