You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@struts.apache.org by mu...@apache.org on 2009/02/05 17:55:15 UTC
svn commit: r741179 - in /struts/struts2/trunk/core/src:
main/java/org/apache/struts2/components/
main/java/org/apache/struts2/views/jsp/ site/resources/tags/
test/java/org/apache/struts2/views/jsp/
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 -->
*
- * <s:iterator status="stat" value="{1,2,3,4,5}" >
- * <!-- grab the index (start with 0 ... ) -->
- * <s:property value="#stat.index" />
- *
- * <!-- grab the top of the stack which should be the -->
+ * <s:iterator var="counter" begin="1" end="5" >
* <!-- current iteration value (1, ... 5) -->
* <s:property value="top" />
* </s:iterator>
@@ -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 -->
+ *
+ * <s:iterator value="{1,2,3,4,5}" begin="2" end="4" >
+ * <!-- current iteration value (2,3,4) -->
+ * <s:property value="top" />
+ * </s:iterator>
+ *
+ * <!-- 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();