mirror of
https://github.com/apache/struts.git
synced 2026-08-11 01:27:14 +00:00
WW-2984 Improve iterator tag to support "begin", "end" and "step" attributes
git-svn-id: https://svn.apache.org/repos/asf/struts/struts2/trunk@741179 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
@@ -23,6 +23,8 @@ package org.apache.struts2.components;
|
||||
|
||||
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.util.MakeIterator;
|
||||
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 @@ import com.opensymphony.xwork2.util.ValueStack;
|
||||
* <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 @@ import com.opensymphony.xwork2.util.ValueStack;
|
||||
*
|
||||
* <!-- 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 @@ import com.opensymphony.xwork2.util.ValueStack;
|
||||
* <!-- 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 @@ public class IteratorComponent extends ContextBean {
|
||||
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 @@ public class IteratorComponent extends ContextBean {
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ public class IteratorComponent extends ContextBean {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,9 @@ public class IteratorTag extends ContextBeanTag {
|
||||
|
||||
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 @@ public class IteratorTag extends ContextBeanTag {
|
||||
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 @@ public class IteratorTag extends ContextBeanTag {
|
||||
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;
|
||||
|
||||
@@ -33,6 +33,22 @@ Please do not edit it directly.
|
||||
<th align="left" valign="top"><h4>Type</h4></th>
|
||||
<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>
|
||||
@@ -49,6 +65,14 @@ Please do not edit it directly.
|
||||
<td align="left" valign="top">Boolean</td>
|
||||
<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>
|
||||
|
||||
@@ -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 @@ public class IteratorTagTest extends AbstractUITagTest {
|
||||
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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user